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.