playhtml.init() options
Every option below can be passed either to playhtml.init({…}) (vanilla) or to <PlayProvider initOptions={{…}}> (React). The underlying InitOptions interface is the same.
import { playhtml } from "playhtml";
playhtml.init({
room: "my-room",
cursors: { enabled: true },
// …
});
Type: string Default: window.location.pathname + window.location.search
The room to connect users to — users sharing a room share state. Every room is automatically prefixed with window.location.hostname so your rooms can never collide with another site’s rooms.
If you leave this blank, playhtml derives the room from the URL. Two readers on /docs/capabilities share state; a reader on /docs/concepts is in a different room.
Override it when you want to decouple state from the URL — for example, a site-wide guestbook that should behave the same no matter which page it’s embedded on:
playhtml.init({ room: "global-guestbook" });
Type: string Default: the public playhtml PartyKit host
Pass your own PartyKit host if you want to self-host the syncing server for extra control, custom server-side logic, or data-residency requirements.
playhtml.init({
host: "mypartykit.user.partykit.dev",
});
You’re responsible for deploying a compatible PartyKit worker — see the playhtml repo for the current worker implementation.
events
Section titled “events”Type: Record<string, PlayEvent> Default: undefined
Declare event listeners inline at init time. Handy for events that should always be wired up.
playhtml.init({
events: {
confetti: {
type: "confetti",
onEvent: () => window.confetti({ particleCount: 100 }),
},
},
});
You can also register events imperatively later with playhtml.registerPlayEventListener. See Events.
extraCapabilities
Section titled “extraCapabilities”Type: Record<string, ElementInitializer> Default: undefined
Ship your own can-* capability alongside the built-ins. Most authors never need this — use can-play on individual elements first. Reach for extraCapabilities when you’re packaging a capability you want to reuse across many elements and want the shorter can-mything attribute form.
playhtml.init({
extraCapabilities: {
"can-pulse": {
defaultData: { on: false },
updateElement: ({ element, data }) => {
element.classList.toggle("pulsing", data.on);
},
onClick: (_e, { data, setData }) => setData({ on: !data.on }),
},
},
});
defaultRoomOptions
Section titled “defaultRoomOptions”Type: DefaultRoomOptions Default: undefined
Configuration for the auto-derived URL-based room. Most apps don’t need to touch this.
onError
Section titled “onError”Type: () => void Default: undefined
Callback for connection failures. Handy for showing an error UI or logging to your own monitoring system.
playhtml.init({
onError: () => {
document.body.classList.add("playhtml-offline");
console.warn("playhtml could not connect");
},
});
developmentMode
Section titled “developmentMode”Type: boolean Default: false
Enable the in-page devtools panel. Shows element inspector, live data tree, connection status, and tag-type badges — modeled after RollerCoaster Tycoon’s inspect UI. Useful while debugging.
playhtml.init({ developmentMode: true });
cursors
Section titled “cursors”Type: CursorOptions Default: undefined (cursors disabled)
Opt into multiplayer cursors, presence identity, chat, and proximity detection. The full set of cursor options is documented on the Cursors page; this is the top-level shape:
interface CursorOptions {
enabled: boolean;
room?: "page" | "domain" | "section" | ((ctx) => string);
container?:
| HTMLElement
| string
| (() => HTMLElement | null)
| React.RefObject<HTMLElement>;
shouldRenderCursor?: (presence) => boolean;
getCursorStyle?: (presence) => Partial<CSSStyleDeclaration>;
playerIdentity?: PlayerIdentity;
proximityThreshold?: number;
onProximityEntered?: (identity, positions, angle) => void;
onProximityLeft?: (connectionId) => void;
visibilityThreshold?: number;
enableChat?: boolean;
onCustomCursorRender?: (connectionId, element) => Element | null;
}
container is only needed when your framework swaps document.body on navigation (Astro ViewTransitions, htmx boost, Turbo). Mark the container with the framework’s persist directive (e.g. transition:persist) and cursors survive navigation. See navigation for examples.
Minimal opt-in:
playhtml.init({ cursors: { enabled: true } });
Domain-wide presence with page-specific cursors (common pattern):
playhtml.init({
cursors: {
enabled: true,
room: "domain",
shouldRenderCursor: (p) => p.page === window.location.pathname,
},
});
See Cursors for the full config reference and recipes.
React equivalent
Section titled “React equivalent”Every option on this page is passed through initOptions on <PlayProvider>.
import { PlayProvider } from "@playhtml/react";
<PlayProvider
initOptions={{
room: "my-room",
cursors: { enabled: true },
developmentMode: true,
}}
>
{/* your app */}
</PlayProvider>;
No React-specific options on the provider itself — all config flows through the shared InitOptions shape.