logo
Unlock

pinball · pinball

PinballPlayfield

1
Listens toPinballEditor

Install with the CLI:

bjs download PinballPlayfield

PinballPlayfield

The painted wood under the glass, drawn from code.

What it does

A real playfield is printed plywood under a hard clear coat, with translucent plastic inserts set into it and a lamp under each. This component paints that once, with a 2D canvas, and lays it on the table as a glossy ground at y = 0.

Everything is placed in table coordinates, [x, z], the same numbers the scene uses for the bumpers and ramps. So a ring you put at a bumper's x and z lands under that bumper, whatever size the texture comes out.

The coat is a physically based material with a low roughness. That is the part ScreenSpaceReflections needs: a shiny surface for the ball, the posts and the bumpers to show up in.

Use it in a scene

A scene is just data — a list of entities, each with its components. At startup the SceneLoader turns this JSON into a live world; edit the file and reload to rebuild it.

Build it from scratch with the bjs CLI:

  1. 1Scaffold a project
    npm create @babylonjsmarket/arcade@latest my-game

    World, renderer, and dev server — ready to run.

  2. 2Install dependencies
    cd my-game && npm install
  3. 3Add PinballPlayfield
    bjs download PinballPlayfield

    Copies its source into src/ so the scene resolves.

    First time? Run bjs login once.

  4. 4Paste the scene into src/scenes/arcade-room.ts and run
    npm run dev

    SceneLoader builds the world from the JSON; reload to rebuild.

JSON
{
  "entities": {
    "Playfield": {
      "components": {
        "MeshPrimitive": {
          "primitive": "box",
          "width": 16,
          "height": 0.2,
          "depth": 78,
          "position": [0, -0.1, 0],
          "visible": false
        },
        "Physics": {
          "shapeType": "box",
          "motionType": "static",
          "restitution": 0.1,
          "friction": 0.3
        },
        "PinballPlayfield": {
          "width": 16,
          "depth": 78,
          "inserts": [
            {
              "kind": "halo",
              "at": [-2.7, 0],
              "size": 1.1,
              "color": [1, 0.35, 0.65]
            },
            {
              "kind": "arrow",
              "at": [-5.6, 24],
              "size": 1.4,
              "angle": -0.1,
              "color": [0.3, 0.9, 1]
            },
            {
              "kind": "text",
              "at": [-0.9, 4],
              "size": 1.6,
              "text": "TOWER",
              "color": [1, 0.8, 0.3]
            }
          ]
        }
      }
    }
  }
}

Hide the playfield box's mesh with "visible": false and keep its physics. Two surfaces at the same height flicker.

Props

  • width, depth (numbers) — the table's size. Match PinballTable.
  • pixelsPerUnit (number, default 48) — texture detail. 48 on a 16 × 78 table is 768 × 3744 pixels. Capped at 8192 on a side.
  • topColor, bottomColor (RGB 0..1) — the base coat at the far end and at the player's end.
  • stripeColor (RGB), stripes (number, default 6) — neon pinstripes up the table.
  • grain (0..1, default 0.25) — flecks in the clear coat. seed keeps them the same every load.
  • inserts — what is painted on. Each has a kind, an at of [x, z], and a size and color:
    • halo — a lit ring, for under a bumper or a saucer. size is its radius.
    • lamp — a round insert, for a rollover lane.
    • arrow — a chevron pointing at a shot. angle in radians; 0 points up the table.
    • text — words painted flat. size is the letter height.
    • band — a soft stripe across the whole table at z. size is how deep it is.
    • image — a picture printed into the wood. src is a URL or a data: URI, size is how wide it is, and its height follows the picture's shape. alpha (default 0.85) lets the wood show through. The floor goes on at once and is painted again when the picture loads. A picture from another site must allow CORS, or it is left out.
  • roughness (default 0.18), metallic (default 0) — the clear coat.

Events

  • Emits pinballPlayfield.painted — { entityId, width, height } in texture pixels, once the art is on.
  • Listens for pinballPlayfield.repaint — { entityId? }, paint again (after changing inserts).

Notes

  • With no DOM (a headless test, a worker) it paints nothing and says nothing.
  • The art is a JPEG data URL, so there is no file to download or host.

More like this

Was this page helpful?

We read every note — tell us what's working and what isn't.

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search