npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gama3d

v0.43.0

Published

GAMA — Gaming And Motion Agent: a 3D game development library built on top of three.js, focused on game loops, entities, steering-based motion agents, animation, cameras and input.

Downloads

4,100

Readme

GAMA — Gaming And Motion Agent

CI npm

GAMA is a 3D game development library built on top of three.js. It gives you the pieces three.js deliberately leaves out — a game loop, entities and components, input, cameras, tweening, gameplay collisions — and its signature feature: motion agents, a composable steering-behavior system for bringing NPCs, enemies, flocks and companions to life.

three.js renders. GAMA makes it a game.

Vision

Most web games start the same way: a requestAnimationFrame loop, a pile of ad-hoc update() calls, keyboard flags scattered across event listeners, and enemy movement written as one-off vector math. GAMA packages those patterns into a small, typed, tree-shakeable library so you start at the gameplay layer, not the plumbing layer.

Design principles:

  • three.js-native, not a wrapper. A GameObject is a THREE.Object3D. Anything from the three.js ecosystem — loaders, materials, postprocessing — works unchanged. GAMA never hides the renderer or the scene graph from you.
  • Motion is the product. Character movement, steering-driven AI, animation cross-fades, camera rigs and tweens are first-class, because motion is what makes a 3D scene feel like a game.
  • Composable behaviors over inheritance trees. An enemy is a GameObject + MotionAgent + a few weighted steering behaviors + a StateMachine. Swap behaviors at runtime to change how it moves.
  • Small and honest scope. GAMA ships gameplay-level collisions (spheres, boxes, triggers) in core, not a physics engine. When you need rigid-body dynamics, the optional gama3d/rapier adapter binds rapier bodies and a stair-climbing character controller to GameObjects — core stays dependency-free either way.

Install

npm install gama3d three

Start from a template

The fastest way to a game that is actually a game — title screen, settings that persist, pause, results, best score, phone controls and a deployable build, all already wired:

node scripts/new-game.mjs my-game     # or --template courier
cd my-game && npm install && npm run dev

| template | what you get | |---|---| | starter | A complete small game (find five markers before the clock runs out) with the whole shell wired. Delete the round, keep the rest. | | courier | Havenbrook Courier — a generated village, a delivery loop, townsfolk in the way, day turning to dusk. A worked example; the docs site serves it at /play/. |

Both depend on the published packages rather than this repo, so what you scaffold is exactly what an outside developer gets. See the shell & templates.

Quick start

import { Mesh, BoxGeometry, MeshStandardMaterial, AmbientLight, Vector3 } from 'three';
import { Game, MotionAgent, Seek, CharacterController, FollowCamera } from 'gama3d';

const game = new Game();
game.world.scene.add(new AmbientLight(0xffffff, 1));

// A keyboard-driven player
const player = game.world.spawn('player');
player.add(new Mesh(new BoxGeometry(), new MeshStandardMaterial({ color: 0x60a5fa })));
player.addComponent(new CharacterController(game.input, { speed: 8 }));

// An enemy that chases the player
const enemy = game.world.spawn('enemy');
enemy.add(new Mesh(new BoxGeometry(), new MeshStandardMaterial({ color: 0xf87171 })));
enemy.position.set(10, 0, 10);
enemy
  .addComponent(new MotionAgent({ maxSpeed: 5, planar: true }))
  .addBehavior(new Seek(player.position));

// A camera that follows the player
const cam = new FollowCamera(game.camera, player, { offset: new Vector3(0, 8, 12) });
game.onUpdate((time) => cam.update(time.delta));

game.start();

Run the bundled demos:

npm install
npm run dev          # player + pursuing chasers + flock + pickups (F3 = debug overlay)
npm run dev:flock    # 400 boids: spatial hashing, obstacle avoidance, containment
npm run dev:navmesh  # click-to-move: A* + funnel pathfinding around walls
npm run dev:physics  # rapier: stairs, ramps, crate pyramid, jumping character
npm run dev:ai       # behavior-tree guards: patrol → chase → give up
npm run dev:navgen   # navmesh BAKED from level boxes + orbit camera
npm run dev:react    # react-three-fiber: 120 boids as declarative JSX

Documentation

Docs site with live playground — run it locally with npm run site:dev, or npm run site:publish to build it and push it to the docs branch for GitHub Pages (Settings → Pages → Deploy from a branch → docs, / (root)). It includes every guide below plus a dozen editable, runnable examples of steering, flocking, navmesh baking, behavior trees and more.

  • Getting started
  • The shell & templates — the part of a game that is not the game
  • Levels: prefabs & format — a scene as data, and the round trip back
  • The editorEditor + gama3d/editor: selection, snapped edits, undo that merges a drag into one step, and mountEditor for a whole tool in one call
  • The asset pipeline — a generated manifest (keys, byte sizes, groups, hashes), AssetLibrary (byte-weighted progress, shared instances, reference-counted release) and a --check gate
  • Networkinggama3d/net: an authoritative server, client-side prediction, reconciliation, entity interpolation, delta snapshots, and a simulated link that makes all of it testable without a socket
  • Dialogue — conversations as JSON so lintDialogue can read them: dangling links, unreachable lines and misspelt variables found before a player finds them; hidden vs locked choices, mid-conversation saves, exact counters
  • The perf gatenpm run perf: calibration-relative timing with honest noise handling, exact work counters, render budgets in headless Chromium — and the deliberate regressions it was made to fail on
  • Using all three libraries — the catalog seam: how GAMA, SCENA and ANIMA compose into one game without importing each other
  • Characters — templates: third-person/top-down players, guards, companions, flocks, locomotion
  • Motion agents & steering — behaviors, flocking at scale, avoidance, state machines, tuning
  • Core — loop, fixed timestep, entities, events, pooling
  • Animation — tweens, easing, clip cross-fades
  • Gameplay — input & actions, camera, collisions, audio, assets
  • AudioSoundboard: procedural sound from a seed — footsteps, impacts, engines, weather, crowds, captions
  • Game feel & HUDGameFeel (trauma shake, hit-stop, slow-mo, rumble) and Hud (score, hearts, banner, prompt, captions, radar)
  • The pickup loopCollector (sweep, values, respawn timers) and CheckpointRun (order enforced, laps, progress) over SCENA-shaped props
  • StakesHealth (i-frames, death as an edge, knockback vectors) and Projectiles (pooled instanced shots, teams, arcs)
  • OppositionHarass (ring-keeping, seeded strafe) and WaveDirector (staggered waves, rest, rubber-band pressure)
  • RetentionGameFlow (title/playing/paused/results, gate() as the pause), Objectives, SaveSlot (versioned, corruption-safe), ghosts (GhostRecorder/Ghost — race yesterday's you)
  • PlatformerPlatformerController: gravity, coyote time, jump buffering, variable height, moving-platform carry — and the coin-run payoff demo
  • Light as gameplayIllumination (the how-lit-am-I field over structural sources), Flashlight (battery, gutter, cone reveal test), MoodGrade (lerped lighting moods per game state)
  • FlightFlightController: arcade-honest fixed-wing flight (bank-to-turn, stall, taxi/takeoff/touchdown events) bridging to SCENA airframes via aircraftInput
  • Physics — the optional rapier adapter: rigid bodies & character controller
  • React — the optional react-three-fiber bindings: <Entity>, useComponent, flock hooks

Architecture

┌───────────────────────────────────────────────────────────┐
│ Game       loop · fixed timestep · renderer · input       │
├───────────────────────────────────────────────────────────┤
│ World      THREE.Scene + GameObject registry              │
│ GameObject extends THREE.Object3D + components + events   │
│ Component  onAttach / update / fixedUpdate / onDetach     │
├────────────────────┬───────────────┬──────────────────────┤
│ Motion             │ Animation     │ Gameplay             │
│ MotionAgent        │ Tween/Tweens  │ Input + ActionMap    │
│  Seek/Flee/Arrive  │ easing        │  (keyboard, gamepad) │
│  Pursue/Evade      │ Animator      │ CharacterController  │
│  Wander            │  (mixer +     │ FollowCamera         │
│  Separation        │   crossfade)  │ OrbitRig/ShoulderRig │
│  (cont.)           │               │ Sphere/BoxCollider   │
│  Alignment         │               │ CollisionSystem      │
│  Cohesion          │               │  (enter/exit events) │
│  FollowPath        │               │ Pool                 │
│  ObstacleAvoidance │               │ AudioManager         │
│  Containment       │               │ Assets               │
│ SpatialGrid        │               │ DebugOverlay         │
│ NavMesh (A*+funnel)│               │                      │
│ NavMeshAgent.goTo  │               │                      │
│ generateNavMesh    │               │                      │
│ StateMachine       │               │                      │
│ BehaviorTree       │               │                      │
├────────────────────┴───────────────┴──────────────────────┤
│ gama3d/rapier (optional entry point, peer dep on rapier)    │
│ PhysicsWorld · RigidBody · PhysicsCharacterController     │
├───────────────────────────────────────────────────────────┤
│ gama3d/react (optional entry point, peer deps react + r3f)  │
│ GamaProvider · Entity · useComponent · useFlockGrid       │
├───────────────────────────────────────────────────────────┤
│ gama3d/templates (characters in one call)                   │
│ createThirdPersonCharacter · createTopDownCharacter       │
│ createGuard · createCompanion · createFlock · Locomotion  │
└───────────────────────────────────────────────────────────┘
                          three.js

Motion agents

A MotionAgent is a component that steers its GameObject by summing weighted forces from classic Reynolds steering behaviors. Behaviors take fixed points, live Vector3 references, getters, or other agents — so targets can move.

import { MotionAgent, Pursue, Evade, Wander, Separation, Alignment, Cohesion, FollowPath, Path, StateMachine } from 'gama3d';

// A guard that patrols, then chases when the player gets close
const agent = guard.addComponent(new MotionAgent({ maxSpeed: 4, planar: true }));
const patrol = new FollowPath(new Path([wp1, wp2, wp3], true));
const chase = new Pursue(playerAgent);

const brain = guard.addComponent(
  new StateMachine({ agent })
);
brain
  .addState({
    name: 'patrol',
    enter: ({ agent }) => { agent.clearBehaviors(); agent.addBehavior(patrol); },
    update: ({ agent }) => {
      if (agent.position.distanceTo(player.position) < 8) brain.setState('chase');
    },
  })
  .addState({
    name: 'chase',
    enter: ({ agent }) => { agent.clearBehaviors(); agent.addBehavior(chase); },
    update: ({ agent }) => {
      if (agent.position.distanceTo(player.position) > 15) brain.setState('patrol');
    },
  });
brain.setState('patrol');

// A flock, in four lines
boid.addComponent(new MotionAgent({ maxSpeed: 3 }))
  .addBehavior(new Separation(() => flock, 1.5), 1.6)
  .addBehavior(new Alignment(() => flock, 5))
  .addBehavior(new Cohesion(() => flock, 6), 0.8);

Available behaviors: Seek, Flee, Arrive, Pursue, Evade, Wander, Separation, Alignment, Cohesion, FollowPath, ObstacleAvoidance, Containment. Implement the one-method SteeringBehavior interface to add your own.

Flocks scale with SpatialGrid, a spatial hash for near-O(n) neighbor queries — examples/flock runs 400 boids with obstacle avoidance:

const grid = new SpatialGrid(5);
game.onUpdate(() => grid.rebuild(flock));
agent.addBehavior(new Separation(grid.near(agent, 5), 1.5), 1.8);

Measured — npm run bench:throughput — the grid overtakes a plain array at around 500 agents (1000 agents: 8.8 ms/frame vs 25.7; 2000: 19.8 vs 91.9). Below that the array is faster and simpler, and the docs say so rather than assuming the fancy structure wins.

Navigation

Point-to-point movement through a level is one call — NavMesh runs A* over triangle adjacency and string-pulls the result with the funnel algorithm:

const nav = NavMesh.fromGeometry(floorGeometry);
enemy.addComponent(new MotionAgent({ maxSpeed: 5, planar: true }));
const navAgent = enemy.addComponent(new NavMeshAgent(nav));

navAgent.goTo(clickPoint);
enemy.events.on('nav-arrived', () => attack());

Physics (optional)

Real rigid-body dynamics via the rapier adapter — a separate entry point, so core gama stays dependency-free:

import { PhysicsWorld, RigidBody, PhysicsCharacterController } from 'gama3d/rapier';

const physics = await PhysicsWorld.create();
physics.attach(game); // steps at the game's fixed rate

crate.addComponent(new RigidBody(physics, {
  collider: { shape: 'box', halfExtents: new Vector3(0.5, 0.5, 0.5) },
}));
const controller = player.addComponent(
  new PhysicsCharacterController(physics, game.input, { speed: 7 })
); // slopes, stairs, snap-to-ground, gravity, jump()

Seeing what agents think

new DebugOverlay(game); // press F3

Velocity arrows (cyan), steering-force arrows (magenta), collider wireframes, and an FPS/entity/draw-call panel — steering bugs stop being invisible.

Animation

import { Tweens, easing, Animator } from 'gama3d';

const tweens = new Tweens();
game.onUpdate((t) => tweens.update(t.delta));
tweens.to(door.position, { y: 4 }, { duration: 0.6, easing: easing.cubicInOut });

// Skinned characters: named clips with cross-fades
const animator = hero.addComponent(new Animator(gltf.animations, gltf.scene));
animator.play('idle');
// later, when the player moves:
animator.play('run', 0.2);

Assets

import { Assets } from 'gama3d';

const assets = new Assets();
assets.onProgress = (loaded, total) => hud.setProgress(loaded / total);
const gltf = await assets.gltf('models/hero.glb'); // cached; repeated calls are free

Roadmap

  • [x] Spatial hashing for Separation/Alignment/Cohesion neighbor queries at scale
  • [x] Obstacle-avoidance (ObstacleAvoidance) and bounds (Containment) steering behaviors
  • [x] Gamepad support and named-action input mapping
  • [x] Audio manager (one-shots, positional audio, music cross-fade)
  • [x] Debug overlay (steering/velocity arrows, collider wireframes, stats)
  • [x] Object pooling and collision enter/exit events
  • [x] Fixed-timestep simulation option
  • [x] Navmesh pathfinding: NavMesh (A* + funnel) and NavMeshAgent.goTo(point)
  • [x] Navmesh generation from level geometry (generateNavMesh: grid sampling, slope/step/radius rules)
  • [x] Rapier adapter (gama3d/rapier): rigid bodies + physics character controller
  • [x] Behavior trees (reactive composites, decorators, typed contexts)
  • [x] Orbit and shoulder camera rigs (drag-orbit + pointer-lock mouse look with occlusion)
  • [x] React-three-fiber bindings (gama3d/react): Entity/GameObject bridge, component hooks, flock grid
  • [x] Documentation site with live, editable playground (npm run site:dev)
  • [x] Character templates (gama3d/templates): third-person & top-down players, guard/companion/flock NPCs, Locomotion animation glue
  • [x] Game templates (gama3d/templates): createRace (whole racer), CricketMatch (ball flight, timing window, laws-accurate scoring)
  • [x] Procedural audio (Soundboard): sample-free synthesized SFX, engines/weather/crowd beds, buses & ducking, captions, offline-render verification
  • [x] Game feel (GameFeel): trauma-squared screen shake, hit-stop, slow-mo with ease-back, haptic rumble
  • [x] HUD (Hud): DOM overlay — score/timer/hearts, banner, objective, prompt, Soundboard caption line, canvas radar
  • [x] Pickup loop: Collector (structural pickups/fields, respawn scheduling) and CheckpointRun (ordered gates, laps, setState painting)
  • [x] Stakes: Health (i-frames, one-shot death edge, revive with mercy window, computed knockback) and Projectiles (pooled tracers, structural targets, team filtering)
  • [x] Opposition: Harass steering (ring + band + seeded strafe) and WaveDirector (trickle spawns, rest, escalation under a ceiling, rubber-band pressure)
  • [x] Retention: GameFlow (legal-move state machine, gate() pause), Objectives (clamped progress, once-only completion), SaveSlot (versioned envelope, null-means-null), GhostRecorder/GhostTape/Ghost (fixed-interval tapes, seam-safe yaw playback)
  • [x] Platformer: PlatformerController (sub-stepped gravity, walls/ceilings, coyote time, jump buffer, variable jump height, moving-platform carry) + the coin-run payoff example
  • [x] Light as gameplay: Illumination field (structural sources, live litness, pure math), Flashlight (battery drama, seeded gutter, cone reveal with angular slack), MoodGrade (structural rig targets, seamless interrupted blends)
  • [x] Flight: FlightController (throttle→speed→lift, bank-to-turn, stall as a state, taxi/rotate/flare/touchdown with sink-rate events, apply() + aircraftInput bridges)
  • [x] Hover: HoverController (collective/cyclic/pedals, rotor spool inertia, seeded hover breath, sink-rate touchdowns, helicopterInput bridge) + rotorVoicing/RotorSound (blade-pass tremolo — the wop-wop is amplitude, not pitch)
  • [x] Dialogue: conversations as data (Dialogue, defineDialogue) with a JSON condition/effect vocabulary chosen so lintDialogue can statically find dangling links, unreachable nodes, strandable choice lists and undeclared variables; hidden vs locked choices, save/restore mid-line, exact counts
  • [x] Air combat: Missiles (lead pursuit under a hard turn-rate limit with speed-bleed — evadability as physics; seeded one-chance flare seduction; pooled instanced) + LockOn (cone/range/time, no credit for past devotion)
  • [ ] Multi-layer navmesh generation (Recast-style voxelization)

Development

npm install
npm test          # 465 vitest unit tests, no browser
npm run typecheck
npm run build     # tsup → dist (ESM + CJS + d.ts)
npm run dev       # vite dev server for examples/basic

And the things that need a browser or a socket, none of which a unit test can stand in for:

npm run verify:playgrounds   # every playground example, headless, pixel-checked
npm run verify:editor        # the editor driven for real, 20 checks
npm run net:check            # two clients over real WebSockets, 10 checks
npm run perf                 # timing + exact counters + render budgets

All of the above run in CI on every push (.github/workflows/ci.yml) — a pipeline that ran npm test and stopped would pass while the playground rendered black, while the editor's raycast missed, and while the netcode dropped a handshake. Each of those was a real bug, and none of them was found by a unit test.

Release notes live in CHANGELOG.md.

License

MIT