function generateSDF_JS_Worker(width, height, path, viewBox, distance, exponent, canvas, x, y, channel) {
  const workerId = 'TroikaTextSDFGenerator_JS_' + ((callNum++) % threadCount)
  let thread = threads[workerId]
  if (!thread) {
    thread = threads[workerId] = {
      workerModule: defineWorkerModule({
        name: workerId,
        workerId,
        dependencies: [
          createSDFGenerator,
          now
        ],
        init(_createSDFGenerator, now) {
          const generate = _createSDFGenerator().javascript.generate
          return function (...args) {
            const start = now()
            const textureData = generate(...args)
            return {
              textureData,
              timing: now() - start
            }
          }
        },
        getTransferables(result) {
          return [result.textureData.buffer]
        }
      }),
      requests: 0,
      idleTimer: null
    }
  }

  thread.requests++
  clearTimeout(thread.idleTimer)
  return thread.workerModule(width, height, path, viewBox, distance, exponent)
    .then(({ textureData, timing }) => {
      // copy result data into the canvas
      const start = now()
      // expand single-channel data into rgba
      const imageData = new Uint8Array(textureData.length * 4)
      for (let i = 0; i < textureData.length; i++) {
        imageData[i * 4 + channel] = textureData[i]
      }
      mainThreadGenerator.webglUtils.renderImageData(canvas, imageData, x, y, width, height, 1 << (3 - channel))
      timing += now() - start

      // clean up workers after a while
      if (--thread.requests === 0) {
        thread.idleTimer = setTimeout(() => { terminateWorker(workerId) }, idleTimeout)
      }
      return { timing }
    })
}
Fallback JS-based implementation, fanned out to a number of worker threads for parallelism