npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@potoru/web

v0.1.1

Published

Potoru on the web: the <potoru-player>, <potoru-snapshot> and <potoru-editor> elements and compile() for .poto stories, in browsers and Node. Heavy parts load on first use.

Readme

@potoru/web

Potoru on the web, in one package: the <potoru-player> element with a control bar, the <potoru-snapshot> element for one still frame, the <potoru-editor> element, and compile(), which turns Potoru source into .poto packages in browsers and in Node. Frames are drawn with Canvas 2D. Pages stay light: engines (interaction, music, path guides), library code, the compiler, the editor UI and the Script sandbox each load only when they are first needed.

From a CDN

<script type="module" src="https://cdn.jsdelivr.net/npm/@potoru/[email protected]/dist/potoru.js"></script>

<potoru-player src="https://example.com/stories/sunrise.poto" controls autoplay muted loop
               style="width: 640px"></potoru-player>

<!-- one frame, no player: thumbnails, storyboards -->
<potoru-snapshot src="https://example.com/stories/sunrise.poto" time="1.5" style="width: 240px"></potoru-snapshot>

<potoru-editor mode="yaml" style="height: 560px"></potoru-editor>

potoru.js registers all three elements. A page with only players fetches no compiler, editor or sandbox code; a page with only snapshots fetches no action or score engine either.

From npm

npm install @potoru/web
import { compile } from "@potoru/web";   // registers the three elements (browsers)

const player = document.querySelector("potoru-player");
player.addEventListener("potoru:ready", (event) => console.log(event.detail.mode, event.detail.duration));
player.addEventListener("potoru:report", (event) => console.log(event.detail.event, event.detail.payload));
player.addEventListener("potoru:error", (event) => console.warn(event.detail.code, event.detail.message));
player.play();
player.seek(1.5);
player.fire("story-door", "open");        // send an event into an interactive story
player.set("story-hero", "speed", 2);     // set a host input

const still = document.querySelector("potoru-snapshot");
still.seek(3);                            // draw another moment

| Entry | What | |---|---| | @potoru/web | registers <potoru-player>, <potoru-snapshot> and <potoru-editor> in browsers and exports compile(), the element classes and the controller; under Node it resolves to core (no registration) | | @potoru/web/player | registers only <potoru-player> and <potoru-snapshot>: no compiler or editor code, even for an app bundler | | @potoru/web/snapshot | registers only <potoru-snapshot>: no controller, clock, sound or controls, and never the action or score engines | | @potoru/web/core | compile, PlayerController, PotoruPlayerElement, PotoruSnapshotElement, PotoruEditorElement, definePotoruPlayer, definePotoruSnapshot, definePotoruEditor, PLAYER_VERSION, PLAYER_CAPABILITIES, types; importing it registers nothing (SSR, Node, frameworks, tests) | | @potoru/web/experimental | the Canvas renderer, surfaces and host: may change in a minor release |

<potoru-player> attributes: src, libraries, autoplay, muted, loop, controls, captions, mode, fit (contain, cover, fill), renderer (canvas, canvas-exact, svg), motion, font-base, camera, still + time (show the frame at time seconds and do not play). Methods: load, play, pause, seek, restart, next, prev, goto, set, fire, setCamera (experimental). Events: potoru:ready, potoru:play, potoru:pause, potoru:ended, potoru:slide, potoru:scene, potoru:report, potoru:caption, potoru:status, potoru:error, potoru:camera.

<potoru-snapshot>: attributes src, time (seconds), libraries, camera, fit, font-base; methods seek(t) and load(src, { libraries }); currentTime, duration; events potoru:ready ({ kind: "snapshot", time, duration, name, width, height, capabilities }) and potoru:error. It draws again when an attribute changes or its box resizes. For many stills, prefer it to <potoru-player still>.

All three elements can be created with document.createElement. The names and events are shared with @potoru/react and other hosts: see docs/COMPONENTS.md in the repository.

compile()

Two source modes:

  • YAML: a format v4 source folder (path → text). Validated and compiled with the same format code as the potoru CLI: the bytes equal what potoru export writes. Browsers and Node.
  • Script: JavaScript that uses the authoring API and returns a Project, run in a sandboxed iframe (opaque origin, no network, hard timeout). Browsers only, JavaScript only in 1.0.
import { compile } from "@potoru/web";

const result = await compile({
  yaml: {
    "potoru.project.yml": "format: potoru\nkind: story\n…",
    "scenes/scene-hill/scene.yml": "…"
  }
});
if (result.ok) await save(result.poto);                 // Uint8Array
else for (const d of result.diagnostics) console.log(`${d.file}:${d.line} ${d.code}: ${d.message}`);

// Script mode (browser):
await compile({ script: `
  import { Project } from "@potoru/authoring";
  const project = Project.create({ id: "dawn", name: "Dawn", format: 4 });
  project.scene("field", { name: "Field", stage: { width: 320, height: 180 }, duration: 1, fps: 30 }, (scene) => {
    scene.rect("sky", { at: [0, 0], size: [320, 120], fill: "#3366cc" });
  });
  export default project;
` });

compile(input) → { ok, poto?, diagnostics, files?, scenes?, sizes }. Options: libraries (.potolib bytes), scene, state, locale, profile: "presentation" (every root State a slide), timeoutMs (Script, default 10 s). It never rejects for bad source; in Node, Script mode returns the diagnostic script-browser-only.

<potoru-editor>

<script type="module">
  import "@potoru/web";                   // registers <potoru-editor> (and the player elements)
  const editor = document.querySelector("potoru-editor");
  editor.yaml = files;                    // compiles once, then on Compile
  editor.addEventListener("potoru:compiled", (event) => console.log(event.detail.sizes));
</script>
<potoru-editor mode="yaml" style="height: 560px"></potoru-editor>

A YAML / Script toggle, the file list, a text editor, diagnostics linked to file:line, a live preview (<potoru-player>) and Compile / Download .poto. Attribute mode; properties yaml, script, lastResult; methods getFiles(), setFiles(), getScript(), setScript(), setMode(), compile(), download(); events potoru:compiled and potoru:error (the compile() result). React: PotoEditor and compile in @potoru/react.

Loading

Everything heavy loads on first use from next to the entry: the action and score engines and library code when a story needs them, the compiler on the first compile(), the editor UI when the first editor connects, the authoring sandbox on the first Script-mode run. The compiler and the player share their chunks, so a page that uses both loads the common runtime once.

Compatibility

Each release lists the capabilities it plays (PLAYER_CAPABILITIES). A package that declares a capability or a container version the player does not know is refused before the first frame with potoru:error code needs-newer-player (also shown in the player): update @potoru/web to the version of the authoring tools that exported the package. Versions move in lockstep with @potoru/authoring, @potoru/render and @potoru/react.

Hosting notes

  • Chunks, wasm and fonts load next to potoru.js (new URL(…, import.meta.url)): serve the whole dist/ folder, with .wasm as application/wasm.
  • With an app bundler (Vite, webpack), the chunks, wasm and the music Worker are copied into your build, but dist/fonts/ is not: copy it to fonts/ next to your page (the elements and compile() look there next), or set font-base to where you serve it.
  • Content Security Policy: script-src 'self' 'wasm-unsafe-eval' (plus the CDN), worker-src 'self' for the music Worker (from another origin, music falls back to the main thread), connect-src for the hosts of your .poto files, and font-src / connect-src for fonts/. Script mode inlines its sandbox into a srcdoc frame, which inherits the page's policy: it needs script-src 'unsafe-inline' there.
  • Browsers with ES2022, Canvas 2D, OffscreenCanvas and WebAssembly (current Chrome, Edge, Firefox, Safari). compile({ yaml }) also runs in Node 20 or newer.

Licence

Apache-2.0 (LICENSE, NOTICE). The wasm engines contain third-party Rust crates (tiny-skia under BSD-3-Clause, rustybuzz and others) and dist/fonts/ holds three fonts under the SIL Open Font License 1.1: see THIRD-PARTY-NOTICES.txt. "Potoru" is a trademark of The Potoru Authors.