@jolly-pixel/engine
v2.5.0
Published
Minimal and opiniated ECS framework on top of three.js.
Readme
📌 About
Minimal and opinionated ECS built on top of Three.js inspired by Superpowers and Craftstudio.
[!WARNING] The engine is still in a heavy phase of development (expect frequent API breaking changes).
💡 Features
- ECS architecture with Actors, Components, and Scenes
- Godot-like Signals
- Behavior scripts
- Input controls (mouse, keyboard, gamepads, touchpad)
- Built-in renderers (3D models, 3D text, sprites, …)
- Asset management
- Audio (background music, sound library, spatial audio)
- UI toolkits for minimal in-game interfaces
💃 Getting Started
This package is available in the Node Package Repository and can be easily installed with npm or yarn.
$ npm i @jolly-pixel/engine
# or
$ yarn add @jolly-pixel/engine🔎 Guides (WIP 🚧)
📚 API
⚙️ Systems
[!TIP] @jolly-pixel/runtime manage most of that for you
Systems are responsible for driving the game loop, orchestrating rendering, and managing shared resources such as assets. They operate on actors and their components each frame.
- World — top-level orchestrator that ties the renderer, scene,
input, and audio into a unified game loop.
- Renderer — abstracts the Three.js render pipeline and supports direct and post-processing render strategies.
- SceneManager — the ECS world manager that owns the actor tree and drives per-frame lifecycle (awake → start → update → destroy).
- Asset — lazy-loading asset pipeline with a registry of loaders, a queue, and a cache.
import { Systems, Actor } from "@jolly-pixel/engine";
const sceneManager = new Systems.SceneManager();
const renderer = new Systems.ThreeRenderer(canvas, {
sceneManager,
renderMode: "direct"
});
const game = new Systems.World(renderer, {
enableOnExit: true,
sceneManager
});
game.connect();🎭 Actor
An Actor is a named node in the scene tree that holds a Transform, a list of Components, and a dictionary of Behaviors. The engine uses the name Actor (inspired by Superpowers) instead of the traditional Entity term.
- Actor — the entity itself, holding its transform, components, and behaviors.
const player = world.createActor("Player");
player.transform.setLocalPosition({ x: 0, y: 1, z: 0 });
const child = world.createActor("Weapon", {
parent: player
});
player.destroy();🧩 Components
Components are pure data and logic units attached to an Actor. They come in three flavours:
- ActorComponent — the base class all components extend (behaviors and renderers are ActorComponent).
- Signals — lightweight pub/sub event emitter for actor-level communication (Godot-inspired signals).
- Renderers — visual components (sprites, models, text, tiled maps) that know how to draw themselves.
- Behavior — script components with a property system and decorator-driven initialization.
import { Behavior, Actor, SignalEvent } from "@jolly-pixel/engine";
export interface PlayerBehaviorOptions {
speed?: number;
}
class PlayerBehavior extends Behavior {
onMovement = new SignalEvent();
speed = 0.1;
constructor(
actor: Actor, options: PlayerBehaviorOptions = {}
) {
super(actor);
this.speed = options?.speed ?? 0.1;
}
update() {
if (this.actor.world.input.isKeyDown("ArrowUp")) {
this.onMovement.emit();
this.actor.transform.moveForward(this.speed);
}
}
}
new Actor(world, { name: "player" })
.addComponent(ModelRenderer, { path: "models/Player.glb" })
.addComponent(PlayerBehavior, { speed: 0.5 });🎮 Device Controls
Aggregates all physical devices (mouse, keyboard, gamepads, touchpad, screen) behind a unified query API so that behaviors can react to player actions without coupling to a specific device.
- Input — central input manager
- CombinedInput — composable input conditions (AND, OR, NOT, sequence) for complex key bindings.
import { InputCombination } from "@jolly-pixel/engine";
const { input } = world;
if (input.isKeyDown("Space")) {
console.log("jump!");
}
const dashCombo = InputCombination.all(
InputCombination.key("ShiftLeft"),
InputCombination.key("ArrowRight")
);
if (dashCombo.evaluate(input)) {
console.log("dash!");
}[!TIP] In ActorComponent or Behavior input are accessible through this.actor.world.input
🔊 Audio
Manages sound playback across the engine. It provides a global volume controller, a factory for creating audio sources, and a playlist-based background music manager.
- Audio — global audio controller owning the
AudioContext and master volume.
- AudioLibrary
- AudioBackground — playlist-based background music with sequential track playback, pause/resume/stop, and playlist chaining.
import { GlobalAudioManager, AudioBackground } from "@jolly-pixel/engine";
const audioManager = GlobalAudioManager.fromWorld(world);
const bg = new AudioBackground({
audioManager,
autoPlay: true,
playlists: [{
name: "main",
onEnd: "loop",
tracks: [
{ name: "theme", path: "audio/theme.mp3" }
]
}]
});
world.audio.observe(bg);
world.audio.volume = 0.5;🖼️ UI
An orthographic 2D overlay drawn on top of the 3D scene. UI elements are anchored to screen edges and support pointer interaction through signals.
- UIRenderer — orthographic camera and CSS2D overlay that drives the UI layer.
Contributors guide
If you are a developer looking to contribute to the project, you must first read the CONTRIBUTING guide.
Once you have finished your development, check that the tests (and linter) are still good by running the following script:
$ npm run test
$ npm run lint[!CAUTION] In case you introduce a new feature or fix a bug, make sure to include tests for it as well.
📦 Internals
License
MIT
