JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 54
  • Score
    100M100P100Q60863F
  • License ISC

A multithreaded 2D web game engine for high-entity-count browser games

Package Exports

  • @weed.js/engine
  • @weed.js/engine/src
  • @weed.js/engine/src/components/AdobeAnimComponent.js
  • @weed.js/engine/src/components/BulletComponent.js
  • @weed.js/engine/src/components/CameraInOutListener.js
  • @weed.js/engine/src/components/Collider.js
  • @weed.js/engine/src/components/CollisionListener.js
  • @weed.js/engine/src/components/DecorationComponent.js
  • @weed.js/engine/src/components/FlashComponent.js
  • @weed.js/engine/src/components/LightEmitter.js
  • @weed.js/engine/src/components/LightOccluder.js
  • @weed.js/engine/src/components/ParticleComponent.js
  • @weed.js/engine/src/components/RigidBody.js
  • @weed.js/engine/src/components/ShadowCaster.js
  • @weed.js/engine/src/components/SpriteRenderer.js
  • @weed.js/engine/src/components/Transform.js
  • @weed.js/engine/src/core/AdobeAnimCompiler.js
  • @weed.js/engine/src/core/AdobeAnimRegistry.js
  • @weed.js/engine/src/core/BigAtlasInspector.js
  • @weed.js/engine/src/core/BulletPool.js
  • @weed.js/engine/src/core/Camera.js
  • @weed.js/engine/src/core/ColliderUtils.js
  • @weed.js/engine/src/core/Component.js
  • @weed.js/engine/src/core/ConfigDefaults.js
  • @weed.js/engine/src/core/Constraint.js
  • @weed.js/engine/src/core/Decoration.js
  • @weed.js/engine/src/core/DecorationPool.js
  • @weed.js/engine/src/core/FSM.js
  • @weed.js/engine/src/core/FSMState.js
  • @weed.js/engine/src/core/Flash.js
  • @weed.js/engine/src/core/Grid.js
  • @weed.js/engine/src/core/Keyboard.js
  • @weed.js/engine/src/core/Layer.js
  • @weed.js/engine/src/core/Mouse.js
  • @weed.js/engine/src/core/NavGrid.js
  • @weed.js/engine/src/core/ParticleEmitter.js
  • @weed.js/engine/src/core/QuerySystem.js
  • @weed.js/engine/src/core/Ray.js
  • @weed.js/engine/src/core/RenderQueueLayout.js
  • @weed.js/engine/src/core/Scene.js
  • @weed.js/engine/src/core/SceneBridge.js
  • @weed.js/engine/src/core/SharedAtomicPool.js
  • @weed.js/engine/src/core/SoundManager.js
  • @weed.js/engine/src/core/SpriteSheetRegistry.js
  • @weed.js/engine/src/core/Sun.js
  • @weed.js/engine/src/core/TileMap.js
  • @weed.js/engine/src/core/debug/DebugDraw.js
  • @weed.js/engine/src/core/debug/DebugFlags.js
  • @weed.js/engine/src/core/debug/DebugUI.css
  • @weed.js/engine/src/core/debug/DebugUI.js
  • @weed.js/engine/src/core/debug/panels/DecorationsPanel.js
  • @weed.js/engine/src/core/debug/panels/EntitiesPanel.js
  • @weed.js/engine/src/core/debug/panels/LayersPanel.js
  • @weed.js/engine/src/core/debug/panels/NavigationPanel.js
  • @weed.js/engine/src/core/debug/panels/PerformancePanel.js
  • @weed.js/engine/src/core/debug/panels/ScenePanel.js
  • @weed.js/engine/src/core/debug/panels/VisualAidsPanel.js
  • @weed.js/engine/src/core/debug/rendering/DebugCanvas.js
  • @weed.js/engine/src/core/debug/rendering/NavDebugRenderer.js
  • @weed.js/engine/src/core/debug/rendering/PhysicsDebugRenderer.js
  • @weed.js/engine/src/core/debug/stats/StatsCollector.js
  • @weed.js/engine/src/core/debug/stubs/DebugDraw.js
  • @weed.js/engine/src/core/debug/stubs/DebugFlags.js
  • @weed.js/engine/src/core/debug/stubs/DebugUI.js
  • @weed.js/engine/src/core/debug/tools/ToolManager.js
  • @weed.js/engine/src/core/debug/ui/DebugDOM.js
  • @weed.js/engine/src/core/decorationFacades.js
  • @weed.js/engine/src/core/gameEngine.js
  • @weed.js/engine/src/core/gameObject.js
  • @weed.js/engine/src/core/gameObjectActiveState.js
  • @weed.js/engine/src/core/sceneBufferMemory.js
  • @weed.js/engine/src/core/sceneSharedBuffers.js
  • @weed.js/engine/src/core/sceneWorkerBootstrap.js
  • @weed.js/engine/src/core/utils.js
  • @weed.js/engine/src/index.js
  • @weed.js/engine/src/lib/pixi-tilemap-module.js
  • @weed.js/engine/src/lib/pixi_8.16_.min.js
  • @weed.js/engine/src/shaders/visibility_polygon.frag.glsl
  • @weed.js/engine/src/shaders/visibility_polygon.vert.glsl
  • @weed.js/engine/src/version.js
  • @weed.js/engine/src/workers/AbstractWorker.js
  • @weed.js/engine/src/workers/AudioMixerProcessor.js
  • @weed.js/engine/src/workers/logic_worker.js
  • @weed.js/engine/src/workers/particle_worker.js
  • @weed.js/engine/src/workers/physics_worker.js
  • @weed.js/engine/src/workers/pixi_worker.js
  • @weed.js/engine/src/workers/pre_render_worker.js
  • @weed.js/engine/src/workers/spatial_worker.js
  • @weed.js/engine/src/workers/visibility/AngularSweep.js
  • @weed.js/engine/src/workers/workers-utils.js

Readme

WeedJS

A multithreaded 2D web game engine for high-entity-count browser games.

WeedJS is built around Web Workers, SharedArrayBuffer-backed component data, and a PixiJS renderer running on OffscreenCanvas. Spatial queries, physics, game logic, particles, render preparation, rendering, and audio mixing each have dedicated execution paths so busy scenes can stay responsive.

Live demo: https://multithreaded-game-engine.vercel.app/demos

WeedJS Demo


Why the Web

WeedJS is designed for developers who want the strengths of the browser as a game platform: open standards, instant URL-based distribution, inspectable source, and a runtime players already have installed.

The engine works with plain JavaScript and browser-native ES modules. The demos can run directly from src/ during development, while the npm package also ships bundled dist/ builds for consumers who prefer package imports.


Architecture at a Glance

WeedJS brings console-style, data-oriented optimization patterns to the browser: pooled objects, dense memory, explicit worker ownership, and predictable frame pipelines. It does not pretend the browser is a console, but it treats the browser runtime with the same seriousness: keep hot data contiguous, avoid unnecessary allocation, move work off the main thread, and measure the result.

WeedJS splits work across specialized workers. Hot frame data lives in typed arrays on SharedArrayBuffer; control flow and setup still use postMessage and MessagePort where that is the right browser primitive.

Worker Count Primary job
spatial_worker 1..N Spatial hash rebuilds and neighbor lists
physics_worker 1 Verlet integration, collision solving, constraints
logic_worker 1..N Entity tick(), lifecycle, collision callbacks
particle_worker 1 Particles, bullets, decals, navigation, visibility lists
pre_render_worker 1 Animation, Y-sorting, render queue assembly
pixi_worker 1 PixiJS rendering on OffscreenCanvas
AudioMixerProcessor 1 Real-time audio mixing on an AudioWorklet thread

The core design rule is single-writer ownership for each shared data region. That keeps most hot paths lock-free and allocation-light while still allowing all workers to read the state they need.


Quick Start

git clone https://github.com/brotochola/MultithreadedGameEngine.git
cd MultithreadedGameEngine
npm install
npm run dev

Open http://localhost:8000/demos/, or use the port printed by the server if 8000 is already in use.

SharedArrayBuffer requires cross-origin isolation headers. The included development server sets the required COOP/COEP headers.


Install from npm

npm i @weed.js/engine
import WEED from '@weed.js/engine';

const { GameEngine, Scene, GameObject, RigidBody, Collider, SpriteRenderer } = WEED;

For local experiments or advanced integrations, the package also exposes the unbundled source modules:

import { Scene, GameObject } from '@weed.js/engine/src';

Minimal Entity Example

You define pooled entities, attach fixed components, and implement lifecycle hooks.

import WEED from '/src/index.js';

const { GameObject, Scene, RigidBody, Collider, SpriteRenderer } = WEED;

class Zombie extends GameObject {
  static scriptUrl = import.meta.url;
  static components = [RigidBody, Collider, SpriteRenderer];

  setup() {
    this.collider.radius = 12;
    this.collider.visualRange = 160;
    this.rigidBody.maxVel = 2.5;
    this.rigidBody.friction = 0.02;
  }

  onSpawned({ x = 0, y = 0 } = {}) {
    this.x = x;
    this.y = y;
    this.setSpritesheet('zombie');
    this.setAnimation('walk_down');
  }

  tick(dtRatio, deltaTime, accumulatedTime, frameNumber) {
    for (let n = 0; n < this.neighborCount; n++) {
      const neighborIndex = this.getNeighbor(n);
      // Neighbors are precomputed by the spatial worker.
    }
  }
}

class ZombieScene extends Scene {
  static config = {
    worldWidth: 5000,
    worldHeight: 3000,
    spatial: { cellSize: 128, maxNeighbors: 500 },
    logic: { numberOfLogicWorkers: 2 },
  };

  static entities = [[Zombie, 20000]];
  static queries = [[RigidBody, Collider]];

  create() {
    for (let i = 0; i < 20000; i++) {
      this.spawnEntity(Zombie, {
        x: Math.random() * 5000,
        y: Math.random() * 3000,
      });
    }
  }
}

const game = new WEED.GameEngine({ debug: true });
await game.loadScene(ZombieScene);

What's Included

WeedJS is intended to be a full 2D game runtime, not just a renderer. The major subsystems are all built around pooled objects, typed arrays, shared memory, worker ownership, and low-allocation hot paths.

  • Pooled ECS-style entities: GameObject instances are facades over typed arrays, with fixed component sets per entity type and reusable spawn/despawn pools.
  • Particle emitter: ParticleEmitter.emit() supports sparks, smoke, blood, muzzle effects, floor decals, alpha/scale/tint controls, gravity, blending, and worker-side particle simulation.
  • Bullets and projectile trails: BulletPool and BulletComponent provide lightweight projectile slots, impact reporting, damage payloads, trail rendering, and visibility culling without turning every shot into a full entity.
  • Decorations and attachments: DecorationPool handles trees, rocks, props, child decorations attached to entities, sway animation, custom anchors, tint, alpha, and Y-sort ordering.
  • Physics: the physics worker uses Verlet integration, velocity/friction/drag controls, gravity, circle and AABB collisions, triggers, static bodies, sleeping bodies, collision layers/masks, and distance constraints for ropes, links, springs, and rigid connections.
  • Spatial hashing: row-owned spatial workers rebuild the grid, cache entity positions, reuse neighbor results when cells have not changed, and expose nearby entities through this.neighborCount / this.getNeighbor(i).
  • Ray casting: Ray.cast, Ray.castWithInfo, Ray.castAll, Ray.linecast, and line-of-sight helpers traverse the spatial grid with DDA and support collision layer masks.
  • Point lights and shadows: LightEmitter, ShadowCaster, LightOccluder, Flash, and Sun support point lights, glow sprites, temporary flashes, ambient lighting, day/night-style sun control, and shadow queues.
  • Layers: built-in layers handle backgrounds, decals, cast shadows, entities, and lighting. Custom layers can route entities, particles, decorations, bullets, trails, and glow sprites into separate render queues.
  • Custom shader layers: custom layers can define fragment shaders, uniforms, blend modes, render-target resolution, and a two-render-texture pipeline for effects like metaballs, fog, heat distortion, glow accumulation, water, and other screen-space passes.
  • Tilemaps: TileMap loads Tiled JSON maps, stores layer data in SharedArrayBuffer, supports allocation-free tile queries from any worker, and renders tilemap backgrounds through the Pixi worker.
  • Rendering: the pre-render worker builds double-buffered render queues, Y-sorts sprites, advances animations, prepares shadows/lights, and feeds a PixiJS renderer running on OffscreenCanvas.
  • Animation: SpriteSheetRegistry, AdobeAnimRegistry, AdobeAnimCompiler, SpriteRenderer, and AdobeAnimComponent cover spritesheets and Adobe Animate-style exports.
  • Navigation: NavGrid provides SAB-backed walkability data, flowfield requests, and A* path requests computed off the logic hot path.
  • Audio: SoundManager uses an AudioWorklet mixer with a shared slot buffer for low-overhead play requests from the main thread or workers, including pitch, volume, loop, pan, and distance attenuation.
  • Input and camera: keyboard, mouse, edge-triggered mouse events, camera follow, zoom, and shared input/camera buffers are available inside workers.
  • FSM helpers: FSM and FSMState support behavior and animation state machines without imposing a specific gameplay architecture.
  • Debugging tools: the debug UI includes worker FPS stats, performance panels, scene/entity/decorations/layers/navigation panels, selected entity inspection, visual aids, physics debug rendering, navigation debug rendering, raycast debug drawing, and configurable debug flags.

Everything performance-critical is aggressively optimized: pooled allocation, dense typed-array component storage, SharedArrayBuffer data paths, single-writer regions, preallocated scratch buffers, compact active/visible lists, double-buffered render queues, worker-side broadphase/physics/render preparation, and benchmark scripts for measuring worker throughput.


Common APIs

// Input
Keyboard.isDown('w');
Mouse.isButton0Down;
Mouse.x;
Mouse.y;

// Camera
Camera.follow(this.x, this.y);
Camera.setZoom(1.5);

// Particles
ParticleEmitter.emit({
  texture: 'spark',
  x: this.x,
  y: this.y,
  speed: { min: 1, max: 3 },
  lifespan: 800,
});

// Flashes
Flash.create({ x: this.x, y: this.y, z: 30, lifespan: 50, color: 0xffaa00, intensity: 10000 });

// Queries inside worker/entity code
const allEnemies = query([RigidBody, EnemyComponent]);
const activeBodies = queryActiveEntities([RigidBody]);
const activeEnemies = queryActiveEntitiesSlow([RigidBody, EnemyComponent]);

Tests and Benchmarks

npm test
npm run test:bench

The benchmark harness uses Playwright and the integrated worker benchmark scene to measure worker FPS, frame timing, and throughput. Performance depends on browser, hardware, scene configuration, and whether cross-origin isolation is active; use the benchmark scripts and tests/bench/BENCHMARK_METHODOLOGY.md when validating changes.


Documentation

Start with docs/README.md for the full docs index.

File Contents
docs/bible_of_weed_js.md Practical quick reference and engine contracts
docs/WORKERS_ARCHITECTURE.md Worker roles, data flow, message protocols
docs/MEMORY_STRUCTURE.md Shared memory layout and ownership map
docs/COMPONENT_STORAGE.md Dense component storage policy
docs/SPATIAL_HASHING.md Spatial grid and neighbor query pipeline
docs/PHYSICS.md Physics worker pipeline and invariants
docs/LAYER_ROUTING.md Render layers, backgrounds, custom layer routing
docs/TILEMAP.md SAB-backed Tiled map API
docs/RAYCASTING.md Grid-based raycast API
docs/ENTITY_TEMPLATE.js Copy-paste entity starter

Package Entry Points

Import Resolves to
@weed.js/engine Bundled dist build
@weed.js/engine/src Unbundled source entry
@weed.js/engine/src/* Direct source subpath imports

License

ISC