Embedding Presentations

Drop a Cuppa Studio deck into any web page. The recommended path is a one-line <iframe> pointing at a share URL — no install, no build, no CDN. Self-hosted options below.

Share URL iframe (recommended)

Every deck has a public share URL (the Share button in the editor copies it to your clipboard — it looks like https://cuppa.studio/p/abc123). Take the id after /p/ and drop it into an iframe:

<iframe
  src="https://cuppa.studio/player-frame?share=abc123&autoplay=true&controls=false"
  width="800"
  height="450"
  style="border: none; border-radius: 8px;"
  allow="autoplay; fullscreen"
  title="My Presentation"
></iframe>

The iframe loads /player-frame on cuppa.studio, fetches the deck JSON via the share API, and renders the player in its own document — which means scaling, fonts, and renderers (Shiki, Mermaid, Chart.js) work the same way they do on cuppa.studio itself, regardless of the host page's CSS.

Query parameters

ParamDefaultDescription
share — Share id (the bit after /p/). Required.
autoplay false Auto-advance through slides silently on load.
autodrive false Auto-narrate using the deck's transcript (loads voice + advances slides as it reads).
controls true Show in-iframe controls (play/pause, scene counter, voice button). Set to false for clean preview thumbnails.

Tip: for small preview thumbnails (under ~500 px wide), the in-iframe controls are hard to use reliably. The common pattern is to set autoplay=true&controls=false, wrap the iframe in an anchor with pointer-events: none on the iframe, and link to the full share URL in a new tab:

<a
  href="https://cuppa.studio/p/abc123"
  target="_blank"
  rel="noopener"
  style="display: block; aspect-ratio: 16/9; position: relative;"
>
  <iframe
    src="https://cuppa.studio/player-frame?share=abc123&autoplay=true&controls=false"
    style="width: 100%; height: 100%; border: 0; pointer-events: none;"
    tabindex="-1"
    title="Preview"
  ></iframe>
</a>

Embedding cuppa.studio's own static decks

If you're linking to a deck that lives on cuppa.studio under /presentations/<slug>/ (the curated library, not a user share), swap share=<id> for src=/presentations/<slug>/. Same query parameters otherwise.

Standalone HTML export

If you need an embed that works without cuppa.studio — for offline use, an email attachment, a private intranet, or a self-hosted static site — export the deck to a single HTML file with the CLI:

mycuppa export slides.cup --out talk.html

The exported HTML bundles the player, the deck, and all assets into one file. It can be:

<iframe
  src="https://example.com/my-talk.html"
  width="800"
  height="450"
  style="border: none; border-radius: 8px;"
  title="My Presentation"
></iframe>

Programmatic control (postMessage)

The iframe accepts postMessage commands from its parent window, so you can drive scene changes or load decks dynamically without a query-string round-trip:

const frame = document.querySelector("iframe");

// Load a CupFile object directly
frame.contentWindow.postMessage({
  type: "load-cupfile",
  cupFile: { manifest, content, theme, timesheet, transcript },
}, "*");

// Jump to a specific scene
frame.contentWindow.postMessage({ type: "go-to-scene", index: 3 }, "*");

// Load a transcript and start voice playback
frame.contentWindow.postMessage({
  type: "load-transcript",
  transcript,
  autoStart: true,
}, "*");

The iframe posts events back to the parent on scene changes, voice state changes, and layout reports — listen for scenechange, voice-started, voice-stopped, and layout-report on window.message.


Roadmap: web component

Status: experimental. Not published to npm yet. Currently being trialled on cuppatech.com and (planned) mycuppa.io as the staging ground. The iframe path above is production-ready and should be used for any live embed today.

<cuppa-cue> is the planned drop-in self-hosted embed for sites that want to avoid an iframe boundary entirely — useful when the host page wants direct DOM access to the player, deeper styling control, or to drop the cuppa.studio runtime dependency. We're holding the npm publish until shadow-DOM scaling and font loading reach parity with the iframe path across the major browsers we care about.

<script type="module">
  // Planned — not published yet
  import "@cuppa-studio/embed";
</script>

<cuppa-cue src="https://cuppa.studio/p/abc123" autoplay></cuppa-cue>

Planned attributes:

AttributeDescription
srcA share URL (/p/<id>) or a static .cup directory URL.
autoplayStart playing immediately on load.

Planned JavaScript API:

const el = document.querySelector("cuppa-cue");

el.goToScene(0);         // Navigate to a scene
el.play();               // Start autoplay
el.pause();              // Pause autoplay
el.getPlayer();          // Access the underlying CuePlayer

el.addEventListener("load",  () => { /* ready */ });
el.addEventListener("error", (e) => { console.error(e.detail); });

If you're keen to try the experimental build before npm publish, the bundle is vendored at cuppatech.com/embed/cuppa-cue.js. Expect rough edges until the roadmap clears.