View as markdown

Iframe embed

The hosted URL works inside an <iframe> — drop it into any page that allows iframes and your agent appears, fully isolated from the host page.

Minimal embed

html
<iframe
  src="https://livelayer.app/a/your-agent-slug"
  width="100%"
  height="720"
  allow="microphone; camera; autoplay"
  style="border: none; border-radius: 12px;">
</iframe>

The allow="microphone; camera" attribute is required — without it the browser blocks the agent from accessing the mic and the visitor sees a permission error.

Floating-bubble pattern

To get a corner-docked widget instead of an inline frame, position the iframe with position: fixed:

html
<iframe
  src="https://livelayer.app/a/your-agent-slug?mode=widget"
  allow="microphone; camera; autoplay"
  style="
    position: fixed;
    bottom: 16px;
    right: 16px;
    width: 400px;
    height: 600px;
    border: none;
    border-radius: 16px;
    box-shadow: 0 12px 40px rgba(0,0,0,0.15);
    z-index: 999999;
  ">
</iframe>

For most floating-widget use cases, the Script tag is better — it's a single line, handles open/close, and remembers state. Use an iframe when you need full isolation (the agent can't see anything in your page).

What an iframe can't do

The hosted URL inside an iframe is sandboxed by browser cross-origin policies. The agent inside cannot:

  • Read text, headings, or form fields on your host page.
  • Fill or submit your forms.
  • Navigate your host page (the iframe can navigate its own contents only).
  • Click elements outside the iframe.

If you need any of that, reach for the NPM package — it embeds in the same DOM as your app and gets full page-context awareness.

Cross-frame messaging (advanced)

The hosted page posts a small set of events to the parent window via postMessage. Listen for them on your page:

ts
window.addEventListener("message", (e) => {
  if (e.origin !== "https://livelayer.app") return;
  if (typeof e.data !== "object" || e.data === null) return;

  switch (e.data.type) {
    case "livelayer:loaded":
      console.log("Agent ready");
      break;
    case "livelayer:conversation:started":
      analytics.track("agent_conversation_started", { id: e.data.id });
      break;
    case "livelayer:conversation:ended":
      analytics.track("agent_conversation_ended", { duration: e.data.duration });
      break;
  }
});

Always validate e.origin before trusting message contents.