To install the KeenASR Java Script library in your web projects perform the following steps:

1. Download keenasr-web SDK

Download the trial SDK and unpack it. The archive contains all the relevant resources: JS library, ASR Bundle, and several examples under examples/ directory.

For initial testing you can point your local web server to this directory and try the examples.

2. Conifigure the Web Server

SSL Server Configuration

Core speech recognition functionality is provided via WebAssembely, which requires the server to be setup with SSL.

Cross-Origin Server Configuration

For security purposes, in order to run Web Assembly (which keenasr-web SDK uses) you will need to configure the server to serve relevant pages in a cross-origin isolation state. Configuration will be server specific, but below we provide suggestion for nginx web server. Other web servers will likely have similar configuration settings.

Content-Encoding for .tgz Files

ASR Bundle is served as a gzipped tar archive (e.g. keenA1m-nnet3chain-en-us.tgz). The SDK expects the browser to unzip the archive, so your web server will need to set Content-Encoding in the response header properly for files ending with .tgz.

For example, if you are using nginx, you will need something like this in your config file:

    location ~* \.tgz$ {
        add_header 'Content-Encoding' 'gzip';
        # Disable nginx's own gzip compression (already compressed)
	gzip off;
	# Set the Content-Type to what it will be AFTER decompression
	default_type application/x-tar;
    }

3. Sample Configuration

If you are using nginx, this config snippet would address the requirements described above:

http {
    include mime.types;

    server {
        listen 80 default_server;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 default_server ssl;

        ssl_certificate /etc/nginx/ssl/cert.pem;  # NOTE - YOU WILL NEED TO SETUP THE CERTIFICATE
        ssl_certificate_key /etc/nginx/ssl/key.pem;

        location / {
            root /usr/share/nginx/html/dist/keenasr-web;

            index index.html;
            autoindex on;

            location ~* \.tgz$ {
                add_header 'Content-Encoding' 'gzip';
	        # Disable nginx's own gzip compression (already compressed)
		gzip off;
		# Set the Content-Type to what it will be AFTER decompression
		default_type application/x-tar;
            }

            # set Cross Origin headers for all HTML and JS files
            location ~* \.(html|js)$ {
                add_header 'Cross-Origin-Embedder-Policy' 'require-corp';
                add_header 'Cross-Origin-Opener-Policy' 'same-origin';
            }
        }
    }
}


If you are using node express web server (for local testing), this index.js file would satisfy all the requirements listed above (including SSL, using self-signed certificate).

import forge from 'node-forge';
import path from 'path';
import express from 'express';
import https from 'https';
import http from 'http';
import serveIndex from 'serve-index';
import { fileURLToPath } from 'url';

/**
 * Generates a self-signed X.509 TLS certificate and RSA private key using node-forge.
 *
 * The certificate is valid for 1 year from the time of creation and is issued for
 * the Common Name (CN) "localhost" with Subject Alternative Names (SAN) for "localhost" and "server".
 *
 * This function is intended for testing or development purposes only. The certificate
 * and private key are returned as PEM-encoded strings and are not written to disk.
 *
 * @returns {{ cert: string, key: string }} An object containing
 *   - `cert`: The PEM-encoded self-signed certificate
 *   - `key`: The PEM-encoded private key corresponding to the certificate
 *
 * @example
 * const { cert, key } = generateSelfSignedCert();
 * https.createServer({ key, cert }, app).listen(4430);
 */
function generateSelfSignedCert() {
  const pki = forge.pki;

  const keys = pki.rsa.generateKeyPair(2048);
  const cert = pki.createCertificate();

  cert.publicKey = keys.publicKey;
  cert.serialNumber = '01';
  cert.validity.notBefore = new Date();
  cert.validity.notAfter = new Date();
  cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);

  const attrs = [{ name: 'commonName', value: 'localhost' }];
  cert.setSubject(attrs);
  cert.setIssuer(attrs);

  cert.setExtensions([
    {
      name: 'subjectAltName',
      altNames: [
        { type: 2, value: 'localhost' }, // DNS
        { type: 2, value: 'server' }
      ]
    }
  ]);

  cert.sign(keys.privateKey, forge.md.sha256.create());

  const certPem = pki.certificateToPem(cert);
  const keyPem = pki.privateKeyToPem(keys.privateKey);

  return { cert: certPem, key: keyPem };
}

// ESM __dirname workaround
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const app = express();
const DIST_PATH = path.join(__dirname, '..', 'dist', 'keenasr-web');

app.use((req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} ${JSON.stringify(req.params)}`);

  // Apply COEP/COOP to all responses
  res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
  res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');

  if (/\.tgz$/.test(req.url)) {
    res.setHeader('Content-Encoding', 'gzip');
  }
  next();
});

// Static files + autoindex
app.use(
  express.static(DIST_PATH, { index: 'index.html' }),
  serveIndex(DIST_PATH, { icons: true })
);

// HTTPS server
const sslOptions = generateSelfSignedCert();
https.createServer(sslOptions, app).listen(443, () => {
  console.log('HTTPS server listening on port 443');
});

// HTTP to HTTPS redirect
http.createServer((req, res) => {
  const host = req.headers.host.split(':')[0];
  res.writeHead(301, {
    Location: `https://${host}${req.url}`,
  });
  res.end();
}).listen(80, () => {
  console.log('HTTP server listening on port 80 (redirecting to HTTPS)');
});