Opal is a browser-based 2D game engine with an integrated editor. Open a tab, sign in, and build — no install and no client build step for game authors. Behavior is authored with Object Flow / Scene Flow (Blueprint-style node graphs), objects use a Unity-like component model, characters can be rigged in Skeleton Studio, and art/audio are prepared in dedicated baker workspaces (Art Canvas, Sound Canvas). Games run in the browser and publish to the arcade, or export as .opal bundles / static standalone players.
Use it: opal-engine.com · Docs: opal-engine.com/docs · Arcade: opal-engine.com/arcade
Open opal-engine.com/create, sign in, create a project, and start editing. Projects save to your account, follow you across machines, and publish to the arcade when you're ready.
npm run dev # editor on :5173 (binds 0.0.0.0 for LAN)
npm run dev:local # same, bound to 127.0.0.1 only
npm test # Node unit suite (src/game + src/app + server tests)
npm run build:player -- path/to/game.opal # static editor-free player → dist/Open http://127.0.0.1:5173/ (or your machine's LAN address on port 5173). Local/dev builds persist through the Node server's data folder (.opal-data/). Without a server API, the editor falls back to IndexedDB.
On a tablet on the same network, open the computer's LAN address; Safari's Add to Home Screen works well for a full-screen editor session.
| Mode | How you reach it | Storage | Best for |
|---|---|---|---|
| Hosted | opal-engine.com/create | Account-backed server | Daily building, publishing, arcade |
| Local dev | npm run dev |
Server data folder | Engine development, LAN testing, bg-removal tooling |
| Static / offline | Editor without /api |
IndexedDB | Offline editing (no account publish) |
User manual, component guides, and reference live at opal-engine.com/docs. Locally:
npm run docs:dev # Author docs with live reload
npm run docs:build:local # Build static docs for /docs/ beside the editorRun docs:build:local once so /docs/ works alongside npm run dev.
Architecture for contributors: docs/ARCHITECTURE.md — vanilla ES modules (no bundler for the editor), layered core < runtime < {behavior, project} < editor < app, with an import-boundary guard so runtime never imports editor (the standalone player depends on that).
Production ops: DEPLOY.md and OPAL_SERVER_RUNBOOK.md cover the self-hosted box (Fedora mini-desktop + Cloudflare Tunnel), auth, env vars, and npm run deploy. Pushing to main does not update the live site — ship with npm run deploy on the box.
The top bar switches what you're editing. The stage and panels adapt to the active workspace:
| Workspace | Primary use |
|---|---|
| Arrange | Scene layout — hierarchy, transform gizmo, inspector, assets |
| Object Flow | Per-object visual scripts |
| Scene Flow | Scene / project rules, startup, win/lose, transitions |
| Motion | Keyframe clips + Motion Machine state machines |
| Sound | Sound Canvas — compose and bake WAV library assets |
| Art | Art Canvas — slice sheets / assemble frames, bake .ssb bundles |
| Rigging | Skeleton Studio — bones, slots, IK, meshes, skins, clips |
| Components | Browse built-ins and author project components |
| Scripts | Project JavaScript helpers / behavior scripts |
| UI | In-game HUD, menus, buttons, labels, layout |
Play mode runs input, physics, Flow, animation, audio, components, and UI in the viewport (or fullscreen). Stop discards the play session and returns to editing.
Debugging: Stats overlay, live Flow overlay, Problems panel, editor console, and Graph Explorer (workbench Graph dock — scene hierarchy, events, prefab anatomy, asset dependencies, component map, runtime trace).
Every object can carry event-driven node graphs — tap, drag, drop, rub, enter area, collision, scene start, and more — and you build behavior by wiring nodes together. Execution flows along white wires; data flows along typed colored wires. You plug values into parameter ports the same way Unreal Blueprints do.
Many fields accept = expressions (for example =self.hp + params.amount on component params). Prefer Get/Set variable nodes for GameState; UI bindings read project variables as self.score.
Scene Flow is the same system at scene/project scope: transitions, win conditions, global variables, timers, and startup logic.
The node catalog grows as you attach components — each component's actions, conditions, and events appear as nodes you can drop into a graph. Project components and scripts can extend that catalog further.
Opal uses a typed component model. Attach built-ins from the inspector, configure fields, and call their actions/conditions from Flow. Components declare lifecycle hooks (onAttach, onDetach, onTick, play start/end) and can require other components (for example, Health Bar pulls in Health).
| Category | Components |
|---|---|
| Core | Motion, Sprite Renderer, Sprite Animator, Sorting Group, Interactable, Tappable, Draggable, Drop Zone |
| Control | Platformer Controller, Top-Down Controller, Side-Scroller Controller, AI Mover |
| Combat | Health, Stats, Health Bar, Ability Cooldown, Combat Action, Turn Queue, Team, Hazard |
| Items | Inventory, Pickup |
| Logic | Timer, Spawner, Projectile |
| Physics (2D) | Rigid Body 2D, Collider 2D, Joint 2D, Ragdoll |
| Scene | Scene Camera |
Full field/action/event reference: docs/components/built-in-reference.md (also on the live docs site).
Author custom components in the Components workspace (Component Studio): fields, events, actions, and conditions via a step-based action builder (set field, call component, emit event, if/else, for each) or inline script bodies. Project components save with the project and register at load beside the built-ins.
2D physics is first-class and backed by Rapier (@dimforge/rapier2d-compat) when available. Typical setup:
- Collider 2D — shape (box, circle, capsule, …), sensor/trigger mode
- Rigid Body 2D — dynamic / fixed / kinematic body
- Joint 2D — constraints between bodies
- Ragdoll — skeleton-driven soft bodies where configured
Collision and sensor events feed Object Flow / Scene Flow. Scene-level physics tuning lives on the scene. See docs/manual/physics-and-collisions.md and docs/physics.md.
Sprite / transform keyframe animation — position, rotation, scale, opacity — with onion skinning, easing, clip looping, scrubbing, and a dope sheet. Motion markers can fire sounds, VFX, shake, and speech at the playhead.
The Motion Machine builds state machines over clips: states (each backed by a motion or skeleton clip), inputs (bool / number / trigger), and transitions (crossfade, on-complete, triggers). Regular motion clips and Skeleton Studio clips share the same machine on an object.
Sprite Animator plays frame sequences (including .ssb bundles from Art Canvas) as a component.
The Art workspace is a frame-animation baker (not a realtime sub-rect renderer). Slice sprite sheets, assemble loose sprites, preview timing, register frames, bake variants, and save reusable .ssb sprite sequence bundles to the library. Consume them from the asset library or Object Flow (Sprite Sequence). The graph persists on the project record (project.artGraph).
Bone-based character animation inspired by Spine:
- Build bone hierarchies (bone, pose, bind, IK, mesh, weights tools)
- Attach art through slots; multiple skins with swappable attachments
- IK constraints — tip + target, chain solves automatically
- Region → mesh conversion and painted vertex weights
- Animate bones, IK targets, slot attachments, colors, draw order, and mesh vertices on a dopesheet with easing
- Save finished characters as reusable character assets
- Drive skeleton clips from the Motion Machine alongside regular motion clips
Setup / Animate mode mirrors Spine's workflow: rig in Setup, pose and key in Animate.
Audio clips are library assets (there is no separate scene-local clip registry). Import WAVs via Add → Sounds, or bake them in Sound Canvas.
Sound Canvas (Sound workspace) is an audio baker — sibling to Art Canvas. Compose from Sample / Mic / Tone / Noise nodes, shape with a processor chain (trim, gain, fade, pitch, reverse, lofi, filter, echo, reverb, normalize, mix, variation), preview, and bake to 16-bit stereo WAV library assets. Baker only: it never runs in the player and never binds onto scene objects. The graph persists on the project (project.soundGraph).
Playback paths:
- Play Audio Flow nodes
- Motion markers (
sound:/sfx:) - Scene music (
scene.audio— music asset + volume), which also plays in the standalone player on boot and scene changes
UI workspace. Author in-game HUDs and menus (panels, text, buttons, bars, sliders, images) with layout, anchors, styling, and bindings to GameState / object state. UI is game content, not editor chrome.
GameState & object variables. Typed variables (number, bool, text) live in two places: project-wide GameState (persists across scenes; Variables panel in the top bar) and per-object variables (inspector Variables card). Flow, expressions, UI bindings, and components read and write them.
Prefabs. Save configured objects (components, motion, Object Flow, children) as reusable blueprints in the asset library. Place from the library or spawn at runtime; per-instance overrides with revert. See docs/prefabs.md and docs/manual/prefabs-and-spawning.md.
Asset Studio (Cutout Tool). Crop, cut out backgrounds, add padding, apply outline or shadow, and save finished assets to the library. Background removal runs locally through a Python / platform backend when the server has models installed (BiRefNet variants, ISNet, U2-Net, BRIA RMBG-2.0, BEN2, or Apple Vision on macOS). Crop / padding / outline / shadow still work without removal models.
VFX. Built-in particle presets (sparkle, smoke, bubbles, magic, confetti, fire, splash, …) triggered from Flow or motion markers, with position, count, duration, and color controls.
Speech. Speech / say bubbles from Flow actions and motion markers (speech: / say:), drawn over objects at runtime.
Optional WebSocket rooms sync objects and network Flow events across clients. Deploy the Cloudflare Worker in workers/opal-room/, paste the URL into the editor Share panel, and connect with a shared room code. See docs/manual/multiplayer.md and workers/opal-room/README.md.
Scenes and projects autosave as you work.
| Path | What it does |
|---|---|
| Publish to Arcade | Hosted portal → public /g/<slug> and /play/<slug> pages (remix optional) |
| Export Game | Full .opal project backup (assets + scenes + GameState + …) |
| Export Scene | One scene as JSON |
npm run build:player -- game.opal |
Static editor-free player folder from an exported .opal |
On opal-engine.com, projects save to your account. Local/dev projects save through the server data folder; without a server API the editor uses IndexedDB. Account login, cross-device storage, arcade publish, and background-removal tooling need the server API; IndexedDB editing and static players do not.
| Path | Purpose |
|---|---|
index.html |
Editor shell |
player.html |
Standalone player shell |
server.cjs |
Zero-dependency Node server — auth, projects, scenes, assets, publishing, background removal |
src/main.js |
Browser entry (boots createEditorApp()) |
src/app/ |
Editor composition root (createEditorApp, bootstrap, frame loop, phases) |
src/player.js |
Standalone player entry (editor-free runtime) |
src/game/ |
Engine — runtime, components, physics, flow/rules, motion, skeleton, UI runtime, audio, VFX, scene editor, studios, persistence |
src/game/physics/ |
Rapier-backed 2D physics runtime |
src/game/components/ |
Component define/host/registry + built-ins + Component Studio |
src/game/flow/ + src/game/rules/ |
Node-graph engine + behavior model/runner |
src/game/scene-editor/ |
Editor UI (inspector, hierarchy, commands, canvases, …) |
src/game/graph-explorer/ |
Graph Explorer workbench (relationship / trace views) |
docs/ |
User manual and reference (VitePress → /docs/ on the live site) |
bundled/ |
Starter assets and demo scenes seeded for new users |
workers/opal-room/ |
Cloudflare Worker + Durable Object for multiplayer rooms |
tools/ |
Background-removal scripts (Python / Swift Vision) |
scripts/deploy.sh |
Production deploy on the opal box (npm run deploy) |
Layer map and dependency rules: docs/ARCHITECTURE.md.
Opal is MIT-licensed and open to contributions. Bug reports, feature ideas, and pull requests are all welcome.
- Fork the repository.
- Create a feature branch.
- Run
npm test(and targeted suites as needed). - Commit your changes.
- Push and open a pull request.
Contributor notes that pay for themselves: ground claims in the current source (not old roadmap docs), prefer node --test + the existing harnesses over manual preview poking, and keep runtime free of editor imports. See CLAUDE.md for local footguns and verification habits.
See LICENSE for details.
