# Embedding a Speak room via iframe


You can embed a Speak room via iframe in any web page: a website, a learning platform, a user account area, or a web app. Participants join the meeting right there without leaving for another site.

You don't need the API or any Kinescope settings for this — just the room link and a snippet of HTML.

> **Информация:**

Inside the iframe, the Speak interface language follows the participant's browser language; link parameters don't change it. You can switch the language inside the room: the **three dots** menu → **Settings** → **General** → **Language**.



## Who this article is for

* **Online schools and learning platforms** — need to run a class right on the course page
* **Event organizers** — need the meeting to take place on the event page rather than via an external link
* **Developers** — need to add video meetings to a user account area or a web app

## How to embed a room

1. **Copy the room link.** Open the room and copy the address from the address bar. During a meeting, the link is also available in the room itself: hover over the room name in the top left corner and click the copy icon in the **Meeting link** row. The link looks like this: `https://speak.kinescope.io/jqi-qhua-glk`. Via the API, the room link and code are returned in the `link` and `code` fields of the `GET /v1/speak/rooms` response — see the [API reference](https://docs.kinescope.com/api/speak/#v1-speak-rooms).
2. **Paste the code into your page** and put your room link into `src`:

```html
<iframe
  src="https://speak.kinescope.io/jqi-qhua-glk"
  allow="camera; microphone; display-capture; fullscreen; autoplay; clipboard-write"
  style="width: 100%; height: 100vh; min-height: 640px; border: 0;"
></iframe>
```

3. **Open the page and test the meeting:** turn on the camera and microphone on the join screen, enter the room, and try sharing your screen. When a participant leaves the meeting, the iframe keeps showing the "You've left the meeting" screen with the **Rejoin** button.

> **Внимание:**

**The `allow` attribute is required.** Without it, the room opens, but the browser won't give it the camera, microphone, or screen sharing — the participant sees "Camera unavailable" or a request to allow the camera and microphone.



### What the allow attribute permits

By default, a browser doesn't let an embedded page use the camera and other devices. The `allow` attribute lists what the Speak room is allowed to use.

| Permission | What it's for |
|---|---|
| `camera` | The participant's camera |
| `microphone` | The participant's microphone |
| `display-capture` | Screen sharing |
| `fullscreen` | The **Full screen** button |
| `autoplay` | Autoplay of other participants' audio and video |
| `clipboard-write` | Copying the meeting link |

Firefox skips `autoplay` and `clipboard-write` and logs a warning about it in the console. This is normal: the camera, microphone, and screen sharing work.

## Page requirements

* **HTTPS only.** The camera and microphone work only on secure pages. If the page is opened over HTTP, you get an error instead of the room. For local testing, `http://localhost` is fine.
* **Iframe size.** The whole join screen fits in an iframe of 1024×640 pixels or larger. In a narrower iframe it switches to a single column, and the camera preview may not fit vertically. It's most convenient to give the iframe the full page width and window height, and the full screen on a phone.
* **Security headers don't block the iframe.** If your site sets `Content-Security-Policy`, `Permissions-Policy`, or page isolation (`Cross-Origin-Embedder-Policy`), check them against the answer to [Why doesn't the iframe load?](#faq-speak-iframe-not-loading)
* **No `sandbox`.** It's better not to set the `sandbox` attribute on the iframe. If you can't do without it, add `allow-scripts allow-same-origin allow-forms allow-popups` to it: without `allow-same-origin` you get a blank screen instead of the room.

## Host access

Inside an iframe, Speak doesn't recognize the room owner: even if you are signed in to Kinescope, the join screen shows **You are a participant**, not **Hosted by you**. The browser doesn't pass sign-in data to an iframe embedded on another site, so everyone in the iframe joins as a participant.

How to run the meeting:

* **The host joins via the direct link**, and participants join through your website. Open `https://speak.kinescope.io/<room code>` in a separate tab: there you have host rights — recording, access control, and the waiting room.
* **The host joins through your website via a link with a token.** Your server signs a JWT token with the `admin` role, and you put a link like `https://speak.kinescope.io/jqi-qhua-glk?token=<token>` into `src`. The role comes with the link, not from sign-in data. How to sign the token is covered in [Speak login link with the administrator role](https://docs.kinescope.com/developer-guides/speak-admin-login-link/).

> **Внимание:**

A link with a token gives administrator rights to anyone who opens it. Generate it on the server and show it only to an authorized host — don't put it on a shared page that everyone sees.



If the waiting room is on, participants in the iframe wait with the message "The host sees your request — please wait". Only a host who is already in the room can let them in: the participant list → **Waiting to join** → **Admit**.

## On phones and tablets

On mobile devices, the iframe first shows the **Get the best experience using our app** screen with app store links and a **Continue in browser** button.

* **To join on your website,** tap **Continue in browser** — the join screen opens, and the camera and microphone work inside the iframe. The choice is remembered for a week.
* **To install the app,** open the room link directly in the browser. Inside the iframe, the App Store and Google Play links may not open: the stores don't allow their pages to be shown in iframes.

## What works differently in an iframe

* **The camera permission request comes from your website.** In Chrome, the request shows your page's domain, not Kinescope.
* **Picture in picture doesn't open in Chrome.** The item is in the **three dots** menu, but nothing happens when you click it: Chrome allows this window only for a page opened directly, not in an iframe. If you need it, open the room via the direct link.
* **RNNoise noise suppression doesn't work.** You can select it in **Settings** → **Audio** → **Noise suppression**, but audio goes without this processing. **WebRTC AEC** (the default) and **DPDFNet (16k)** work in an iframe.
* **The meeting link in the room is the direct one.** The **Meeting link** row in the pop-up by the room name shows and copies the `speak.kinescope.io` address, not your page address. If participants should join through your website, send them a link to your page.

## Troubleshooting

{{< faq >}}
{{< faq-item id="speak-iframe-camera" q="Why does Speak say “Camera unavailable” even though camera access is allowed in the browser?" >}}
This is how Speak shows that the browser doesn't let the iframe use devices. Instead of "Camera unavailable", you may see the request "Allow Kinescope to use your camera and microphone".

Check in order:

1. **The `allow` attribute** — it must include `camera` and `microphone`. Without them, the browser blocks devices in the iframe, and the console shows `Permissions policy violation: camera is not allowed in this document`.
2. **Your site's `Permissions-Policy` header** — if it contains `camera=()` or `microphone=()`, the `allow` attribute won't help. Allow the devices for Speak, for example: `Permissions-Policy: camera=(self "https://speak.kinescope.io"), microphone=(self "https://speak.kinescope.io"), display-capture=(self "https://speak.kinescope.io")`.
3. **The protocol** — the page must open over HTTPS.
{{< /faq-item >}}

{{< faq-item id="speak-iframe-screen-share" q="Why doesn't screen sharing start?" >}}
If the **Share your screen** button does nothing, add `display-capture` to the `allow` attribute. Also make sure your site's `Permissions-Policy` header doesn't forbid `display-capture`.
{{< /faq-item >}}

{{< faq-item id="speak-iframe-blank-screen" q="Why is there a blank dark screen instead of the room?" >}}
The iframe has loaded, but Speak couldn't start. Possible causes:

1. **The browser blocks third-party cookies.** This is the default in Chrome's Incognito mode, and in normal mode it's turned on in the privacy settings. Inside the iframe, Speak can't access browser storage and doesn't start. The participant needs to allow third-party cookies for `speak.kinescope.io` in the browser settings or open the meeting via the direct link.
2. **The iframe has a `sandbox` attribute without `allow-same-origin`.** Remove `sandbox` or add the required values — see the [page requirements](#page-requirements).

> **Совет:**

Put an "Open the meeting in a new tab" link with the direct room address under the iframe. This way a participant can join even if their browser doesn't let Speak run in an iframe.


{{< /faq-item >}}

{{< faq-item id="speak-iframe-error" q="Why is there an error message instead of the room?" >}}
If instead of the join screen the iframe shows a line like `error_boundary ID: …`, most often the page is opened over HTTP. On an insecure page, the browser doesn't give access to the camera and microphone, and the room doesn't start. Move the page to HTTPS.
{{< /faq-item >}}

{{< faq-item id="speak-iframe-not-loading" q="Why doesn't the iframe load?" >}}
If there's an empty rectangle or a browser error where the iframe should be, open the browser console (**F12** → **Console**) and look for the message:

* **`violates the following Content Security Policy directive: "frame-src …"`** — your site's security policy doesn't allow external iframes. Add `https://speak.kinescope.io` to the `frame-src` directive of the `Content-Security-Policy` header.
* **`net::ERR_BLOCKED_BY_RESPONSE`** — the page has isolation turned on: the `Cross-Origin-Embedder-Policy` and `Cross-Origin-Opener-Policy` headers. Speak can't be embedded in such a page. Remove these headers from the meeting page or move the meeting to a separate page without them.
{{< /faq-item >}}

{{< faq-item id="speak-iframe-owner" q="Why does the iframe show “You are a participant” even though I own the room?" >}}
This is expected: in an iframe, the browser doesn't pass your Kinescope sign-in data to Speak, so recording and access control aren't available. Join the room via the direct link or via a link with a token — see [Host access](#host-access).
{{< /faq-item >}}

{{< faq-item id="speak-iframe-waiting" q="Why does a participant wait a long time to join?" >}}
If a participant sees "The host sees your request — please wait" and isn't let in, the waiting room is on and there's no host in the room. The host needs to join via the direct link and admit the participant. If you don't need the waiting room, turn it off in the room: **Access control** → **Waiting room**.
{{< /faq-item >}}
{{< /faq >}}

If that doesn't help, write to the support chat within the Kinescope interface: include the address of the page with the iframe, the room code, the browser name, and a screenshot of the console.

## What's next?

After embedding a room, we recommend:

1. **[What is Speak?](https://docs.kinescope.com/speak-beta/what-is-speak/)** — video meeting capabilities, recording, and the waiting room
2. **[Speak login link with the administrator role](https://docs.kinescope.com/developer-guides/speak-admin-login-link/)** — host access through your website via a link with a JWT token
3. **[Restreaming in Speak](https://docs.kinescope.com/speak-beta/restreaming-in-speak/)** — streaming a meeting to external platforms straight from the room
4. **[Troubleshooting and support](https://docs.kinescope.com/troubleshooting/)** — what to do if something goes wrong with a meeting

Still have questions? Write to the support chat within the Kinescope interface — our specialists will help!

