Skip to content

Getting started

playhtml turns any HTML element into a live, collaborative one with a single attribute. This guide gets you from empty page to a working shared toggle.

Pick your setup:

There are two paths. Pick the one that matches your project.

Path A: drop-in script (no build, no module syntax)

Section titled “Path A: drop-in script (no build, no module syntax)”

The fastest path — no import, no playhtml.init() call. Add the script tag at the end of your <body>, after your interactive elements:

<body>
  <button id="my-lamp" can-toggle>lamp</button>

  <script type="module" src="https://unpkg.com/playhtml/init.js"></script>
</body>

Give every interactive element a unique id. That’s how state is keyed and synced across everyone on the page.

Why end-of-body? init.js runs playhtml.init() immediately, and init() scans the DOM for capability attributes (can-toggle, can-move, etc.) when it runs. Place it after the elements it should find.

Want options like cursors? Use Path B — the drop-in init.js calls playhtml.init({}) with no options.

Path B: import + manual init (when you need options or a bundler)

Section titled “Path B: import + manual init (when you need options or a bundler)”

Reach for this when you want to pass options to init() (cursors, custom rooms, etc.) or you’re already using a bundler.

From a CDN, no build step: put the script at the end of <body> so the elements exist when init() runs.

<body>
  <button id="my-lamp" can-toggle>lamp</button>

  <script type="module">
    import { playhtml } from "https://unpkg.com/playhtml";
    playhtml.init({ cursors: { enabled: true } });
  </script>
</body>

From npm with a bundler:

npm install playhtml
import { playhtml } from "playhtml";
playhtml.init({ cursors: { enabled: true } });

Bundlers usually handle script placement for you; just make sure init() runs after your interactive elements are in the DOM (e.g. on DOMContentLoaded or after your framework mounts).

Either way, give every interactive element a unique id — that’s how state is keyed and synced across everyone on the page.

This toggle is shared with everyone reading this page right now. Click it and watch.

  • Core concepts — the four kinds of shared state (element data, page data, presence, events) and when to use each.
  • Using React — if your app is a React app, start here; concept pages show React inline.
  • Capabilities — every built-in can-* attribute with live demos.
  • Data essentials — shape, update, and clean up defaultData.
  • Presence & cursors — multiplayer cursors and ephemeral per-user state.
  • Shared elements — cross-page and cross-domain state.
  • Building with AI — Claude Code plugin + a prompt template for any LLM.
  • API reference — the full playhtml.init() options table and React API types.