Interactive Avatar: Widget security policies
Allow the widget in your Content Security Policy, Permissions-Policy and referrer settings.
The widget runs in a frame that loads from Synthesia’s origin, and the script snippet loads one script from that same origin. On most sites, the snippet works as soon as you paste it. If your site sends security headers, though, those headers can block the script, the frame or the microphone, and the widget then stays blank or can’t hear the visitor.
Read on if your site sends a Content-Security-Policy, Permissions-Policy, Referrer-Policy or Cross-Origin-Embedder-Policy header, or if your CMS rewrites the HTML you paste. Each section below explains what to change, what you can leave as it is, and what the widget can’t support yet.
Content Security Policy
A Content Security Policy tells the browser which origins your page may load scripts and frames from. If your page sends one, you need to add https://interactive-avatars.synthesia-embeds.io to two directives:
For example:
If your policy uses script-src-elem, add the origin there too. If your policy has no frame-src, browsers fall back to child-src and then to default-src, so add the origin to whichever one your policy has.
Directives you don’t need to change
The rest of your policy can stay as it is. The widget makes its own requests from inside its frame, where the frame’s policy applies and yours doesn’t. runtime.js itself needs nothing beyond script-src. So these directives don’t affect the widget:
Size the iframe without inline styles
The iframe snippet sets its size with an inline style attribute. If your style-src doesn’t allow 'unsafe-inline', the browser ignores that attribute. The frame then falls back to a fixed 400 × 620 and can overflow a phone screen. To avoid this, set the size in your own stylesheet, using the class the snippet carries:
Use a nonce
If your policy uses nonces with 'strict-dynamic', browsers ignore host names in script-src, so adding the widget origin isn’t enough. Put your nonce on the script tag instead:
Under 'strict-dynamic', a script tag that a trusted script inserts, such as a tag added by your tag manager, needs no nonce. runtime.js doesn’t load any other scripts, so there’s nothing else to allow.
If you use the inline stub, it needs your nonce or its hash in script-src, like any other inline script.
Other headers
A few more headers affect how pages load scripts and frames. Here’s how the widget behaves with each of them:
If any of these policies blocks the frame, it stays empty. With the script snippet, your page also gets an internal error.
Microphone and autoplay
The widget frame needs the microphone so visitors can talk to the avatar, and autoplay so they can hear it. Both snippets create the frame with allow="microphone; autoplay". That attribute grants only these two permissions, and only to the widget origin. The browser still asks the visitor before it turns on the microphone, and the widget only asks once the visitor clicks to start a call.
Keep the allow attribute. If you remove it, or a sanitizer or CMS strips it, the browser blocks the microphone.
Permissions-Policy
A frame can never get a permission that its page doesn’t have, whatever the frame’s allow attribute says. So if your page sends a Permissions-Policy header with a microphone or autoplay directive, that directive must list the widget origin. For example, both microphone=() and microphone=(self) block the widget. Add the origin like this:
If your header has no microphone or autoplay directive, or you don’t send the header at all, you don’t need to change anything. The frame’s allow attribute is enough.
When your page’s policy blocks the microphone, what the visitor sees depends on the browser. Chrome and Edge let the widget detect the policy, so the widget tells the visitor that the microphone isn’t allowed on this page. Firefox and Safari don’t expose the policy, so there the widget asks the visitor to allow the microphone. With the script snippet, your page also gets an internal error.
Referrer
The widget checks that it runs on an allowed site. It learns your page’s origin from the referrer and compares it with the deployment’s allowed origins. The frame carries referrerpolicy="strict-origin", so the widget gets your origin but never the path of your page.
With the script snippet, you don’t need to do anything. runtime.js always sets this attribute, so your page’s own referrer policy doesn’t affect the widget.
With the iframe snippet, keep the referrerpolicy attribute that the snippet comes with. Pages with a no-referrer or same-origin policy send no referrer to other origins. Without the attribute, the widget on such a page shows “can’t load on this page” when the visitor starts a call.