Capabilities
Every system, shown as the code you'd actually write.
JGengine is a stack of deep primitives behind narrow surfaces — worlds, entities, combat, netcode, authored scenes, HUDs, assets. Each one is a few lines of intent, not a subsystem you build. Here they are, for real.
Live specimens
Alive, not annotated.
Each demo below is the actual zero-dependency @jgengine/core function running in this tab, feeding three.js — exactly how @jgengine/shell consumes core in a real game. The code beside each one isn't a description of the canvas; it's what the canvas runs. Drive the dials.
windField().atPoint(x, z, t) — sampled on a 12×8 grid, bilinear per blade
world/wind · world/scatter
Wind that moves the grass
The blades are placed by the real scatter() and lean, every frame, toward the real windField sample. The gust fronts rippling across the meadow are the field's own turbulence — no shader fakery, no baked animation.
import { windField } from "@jgengine/core/world/wind";import { scatter } from "@jgengine/core/world/scatter";const blades = scatter({ area: { w: 60, d: 40 }, count: 2600, seed: "meadow" });const wind = windField({ speed: 2.6, gust: 1.6, turbulence: 0.6 });// per frame, lean each blade by the real field vector at its pointconst [wx, wz] = wind.atPoint(blade.x, blade.z, elapsed);// ↑ the meadow beside this code runs exactly this call, per bladedrag either pole — catenaryCurve() re-solves from the live anchors
world/catenary
A cable that actually hangs
Drag either pole. The festoon cord between the tips is the true hyperbolic cosh curve a uniform cable takes under gravity — not a bezier approximation — re-solved from the live anchor positions the moment you move one.
import { catenaryCurve } from "@jgengine/core/world/catenary";// true cosh catenary between the two live pole tipsconst points = catenaryCurve( [poleA.x, 6, poleA.z], [poleB.x, 6, poleB.z], slack, // extra length as a fraction: 0.1 = 10% longer than taut 56,);// drag a pole in the demo → the cable re-solves from these anchorsfractalNoise(x, z, cfg) — displacing all 91×91 vertices in place
world/terrain
Terrain from a single seed
Every vertex height is one call into the core value-noise fractal. Turn octaves, frequency, and ridged, or reseed, and the whole field is rebuilt in place from the same function a shipped game bakes into its heightfield.
import { fractalNoise } from "@jgengine/core/world/terrain";const cfg = { seed, frequency, octaves, lacunarity: 2, persistence: 0.5, ridged };// displace every plane vertex by real fractal value noisefor (const v of vertices) { v.y = fractalNoise(v.x, v.z, cfg);}// the terrain in the demo is this loop, over 91×91 vertices🎮Runtime
Define a whole game as data
One call wires the runtime: assets, world, physics, input, save, and the lifecycle loop. No engine boilerplate, no scene graph to hand-assemble.
import { createAssetCatalog } from "@jgengine/core/scene/assetCatalog";import { defineGame } from "@jgengine/shell/defineGame";import { world, physics } from "./world";export const game = defineGame({ name: "Meadow Run", assets: createAssetCatalog(), world, physics, save: "none", loop: { onInit, onNewPlayer, onTick },});⛰️World & procedural
A world is a place: substrate + laws
Declare the place you play in — flat, round, voxel, or board ground with its own physics. Sky look, foliage, and props are authored in the editor's scene document, not coded onto the world.
import { world } from "@jgengine/core/world/place";export const moon = world({ id: "moon", ground: { mode: "round", size: { radius: 300 } }, physics: { gravity: -4 },});// Sky + scatter live in editor.scene.json; seeds derive from id + save.🧩Scene & entities
Spawn and simulate entities
A per-world entity store you spawn into and tick. State serializes cleanly and scales to many entities and many players — no module-global maps.
export function onNewPlayer(ctx: GameContext) { ctx.scene.entity.spawn("player", { id: ctx.player.userId, position: SPAWN });}export function onTick(ctx: GameContext, dt: number) { for (const mob of ctx.scene.entity.list()) { if (mob.role !== "npc") continue; const [x, y, z] = mob.position; ctx.scene.entity.setPose(mob.id, { position: [x + mob.velocity[0] * dt, y, z], dt }); }}⚔️Combat
Abilities, projectiles, and death
Cast runners, resource meters, auto-target, projectiles, and a death system — feel and resistance included. Opt in per game; a puzzle game never carries it.
import { createAbilityKit } from "@jgengine/core/combat/abilityKit";import { createDeathSystem } from "@jgengine/core/combat/death";const abilities = createAbilityKit([ { id: "fireball", cooldownMs: 1500, resourceCost: 20, castType: "projectile" },]);// canCast/cast track cooldowns, charges, and resource spend for youif (abilities.canCast("fireball").ok) abilities.cast("fireball");const death = createDeathSystem({ resolveOnDeath: (id) => ({ drops: "goblin-loot" }), resolveIdentity, loot, events, despawn: (id) => scene.entity.despawn(id),});🌐Multiplayer
Authoritative host, one protocol
A browser-safe authoritative host and client backend over a pluggable transport pipe — WebSocket, WebRTC P2P, or loopback — with Postgres/Convex persistence behind the same seam.
import { createWsBackend } from "@jgengine/ws/createWsBackend";const backend = createWsBackend({ url: "wss://your-host.example/game", userId: player.id,});const { serverId } = await backend.createSession({ gameId: "meadow-run" });// Reconnect, snapshot sync, and save cadence are handled for you.🗺️Authored scenes
Place it once, render it generically
Author paths, foliage, and gameplay spots in the 3D editor, save editor.scene.json, and drape it at runtime — the render and the gameplay read the same document.
import { AuthoredScene } from "@jgengine/shell/scene";<AuthoredScene document={doc} field={ctx.world.ground} />;// Enemy waypoints and tower plots derive from the same doc —// coordinates live once, editable in the editor.🎛️UI & HUD
Headless HUDs, drag-to-place
React hooks expose game state; HudPanels auto-stack, clear the touch dock, and are laid out live with the F2 canvas editor. No fixed overlay is ever forced on your game.
import { useCurrency, useAbilitySlot } from "@jgengine/react/hooks";function Hud() { const gold = useCurrency("gold"); const fireball = useAbilitySlot(abilities, "fireball"); const cd = Math.ceil((fireball?.cooldownRemainingMs ?? 0) / 1000); return <HudPanel anchor="top-right">{gold} · {cd}s</HudPanel>;}📦Assets
License-verified CC0 models
A typed, license-checked index of CC0 3D packs. Pull the models you name, register them in a catalog, and place them — no hunting for assets or licenses.
# discover and pull CC0 packs, license-verifiednpx jgengine assets search "tree"npx jgengine assets pull quaternius-stylized-natureAnd the rest
Loot, quests, economy, crafting, turns, inventory, social…
Genre systems are all opt-in primitives on the same core. Your agent pulls in only the domains your game needs — and nothing it doesn't.