Playground Runtime Embedded Demo

This example shows the new reusable Playground(...) runtime layer. It keeps the live-code style API, but without Monaco or the current Playground UI.

The idea is to make code written for Tetorica easier to move into a browser game or another small app.

Pass { execution: "worker" } to pg.play() to keep game code off the main thread. The default remains main-thread execution for compatibility.

Source: web/playground_runtime.js | docs/demos/playground_runtime.html

Minimal Usage

import {
  Playground,
} from "../js/playground_runtime.js";

const pg =
  Playground({
    audioWorkletUrl:
      "../js/ym2612-worklet.js",
    ym2612WasmUrl:
      "../generated/ym2612_wasm.wasm",
    segaPsgWasmUrl:
      "../generated/segapsg_wasm.wasm",
  });

await pg.initialize();

pg.load("stage1", `
setBpm(120);

fm.setPreset(CH1, FM_PRESETS["one-op-basic"]);

liveLoop("lead", async () => {
  await play("C4", {
    channel: CH1,
    duration: 0.18,
  });
  await beat(0.5);
  await play("E4", {
    channel: CH1,
    duration: 0.18,
  });
  await beat(0.5);
  await play("G4", {
    channel: CH1,
    duration: 0.18,
  });
  await beat(1);
});
`);

await pg.play("stage1", {
  execution: "worker",
});
Back
Ready.
{
  "audio": "idle",
  "playback": "stopped",
  "currentSourceName": null,
  "loadedSourceNames": []
}

What This Layer Hides

Application / Game
    |
    v
Playground runtime
    |
    +-- source registry
    +-- play / stop control
    +-- liveLoop helpers
    +-- YM2612 / PSG access
    |
    v
MegaDriveSynth
    |
    +-- AudioContext
    +-- AudioWorklet
    +-- WASM loading
    |
    v
YM2612 / PSG output