{"record":{"id":"b562b266c93b0bca","repo":"heygen-com/hyperframes","slug":"audioworklet-is-unavailable-the-page-needs-a-sec","errorCode":null,"errorMessage":"AudioWorklet is unavailable — the page needs a secure context (https, localhost or file://)","messagePattern":"AudioWorklet is unavailable — the page needs a secure context \\(https, localhost or file://\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/audio/audioFxWorklets.ts","lineNumber":203,"sourceCode":"        this.holds[ch] = (this.holds[ch] + 1) % step;\n      }\n    }\n    return true;\n  }\n}\nregisterProcessor(\"hf-bitcrush\", HfBitcrush);\n`;\n\nlet modulePromise: Promise<void> | undefined;\n\n/**\n * Register the processors on a context. Idempotent per module instance, since\n * addModule throws if the same processor name is registered twice.\n */\nexport function ensureAudioFxWorklets(ctx: BaseAudioContext): Promise<void> {\n  modulePromise ??= (async () => {\n    if (!ctx.audioWorklet) {\n      throw new Error(\n        \"AudioWorklet is unavailable — the page needs a secure context (https, localhost or file://)\",\n      );\n    }\n    // A data: URL rather than a blob:, because a blob inherits the page origin\n    // and is treated as opaque on a file:// page, where the module then fails\n    // to load with an unhelpful AbortError.\n    const url = `data:text/javascript;base64,${btoa(\n      String.fromCharCode(...new TextEncoder().encode(AUDIO_FX_WORKLET_SOURCE)),\n    )}`;\n    await ctx.audioWorklet.addModule(url);\n  })();\n  return modulePromise;\n}\n\n/** Test seam: forget the cached registration so a fresh context can register. */\nexport function __resetAudioFxWorkletsForTests(): void {\n  modulePromise = undefined;\n}","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/core/src/audio/audioFxWorklets.ts#L185-L221","documentation":"ensureAudioFxWorklets() registers the AudioWorklet processors (hf-compressor, hf-limiter, hf-gate, hf-bitcrush) on a BaseAudioContext. If ctx.audioWorklet is undefined it throws, because AudioWorklet only exists in secure contexts. Secure means https://, localhost (127.0.0.1/localhost), or file://. The effects that need worklets are exactly those whose def.web starts with 'worklet-' (see chainNeedsWorklets).","triggerScenarios":"Previewing or rendering a chain that contains a worklet effect while the page is served over plain http on a non-localhost host (e.g. http://192.168.x.y:3000 on a LAN device, or an embedded iframe on http). The browser omits ctx.audioWorklet entirely in insecure contexts, so the property is undefined rather than the call failing.","commonSituations":"Opening the studio preview from another machine on the LAN via http://<lan-ip>; rendering inside an iframe embedded on a non-https parent page; testing on http://0.0.0.0; an older browser lacking AudioWorklet support.","solutions":["Serve the page over https, or access it via localhost/127.0.0.1/file:// so the context is secure.","Remove worklet-based effects (compressor, limiter, gate, bitcrush) from the chain for that context — non-worklet effects (biquad filters, waveshaper, delay, chorus, phaser, convolver) run without AudioWorklet.","If embedding the studio, ensure the parent page is https and the iframe is same-origin or served over https."],"exampleFix":"// before — page on http://lan-ip, chain has worklet effects\nctx.audioWorklet; // undefined -> ensureAudioFxWorklets throws\n\n// after — serve securely or drop worklet nodes\nimport { chainNeedsWorklets } from \"@hyperframes/core\";\nif (chainNeedsWorklets(chain) && !window.isSecureContext) {\n  chain = { ...chain, nodes: chain.nodes.filter((n) => !n.type.startsWith(\"worklet-\")) };\n}\n// and/or access via https:// or localhost","handlingStrategy":"validation","validationCode":"function canUseWorklets(ctx: BaseAudioContext): boolean {\n  return typeof ctx.audioWorklet === \"object\" && ctx.audioWorklet !== null && window.isSecureContext;\n}\nif (chainNeedsWorklets(chain) && !canUseWorklets(ctx)) {\n  // strip worklet nodes or move to https/localhost\n}","typeGuard":"function supportsAudioWorklet(ctx: BaseAudioContext): boolean {\n  return ctx.audioWorklet != null && typeof window !== \"undefined\" && window.isSecureContext === true;\n}","tryCatchPattern":"try { await ensureAudioFxWorklets(ctx); }\ncatch (err) {\n  if (/secure context/.test(String(err))) { /* serve over https/localhost or drop worklet effects */ }\n  else throw err;\n}","preventionTips":["Serve the page over https or access via localhost/file://.","For LAN/embedded contexts, drop worklet effects from the chain.","Check window.isSecureContext before registering worklets."],"tags":["audio-worklet","secure-context","browser","https","runtime"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}