lc Ludic Canvas0.3.2

THREE.JS + LUDIC CANVAS / PLAYABLE 3D EXAMPLE

Kestrel Survey

Read an unfamiliar world. Pilot a small rover across a procedural desert, visit three mineral sites, and complete your field collection.

Get the example project ↗

Preparing the desert and two MSDF font families...

3D WORLD + A FULLY CUSTOM CANVAS UIInitializing two independent renderers

Two renderers. One game loop.

Three.js owns the perspective camera, terrain, rover, lights, shadows and raycasting. Ludic Canvas draws every game panel, button, map, meter, modal and projected site label on a transparent overlay. The 3D engine uses WebGL 2; the UI has its own WebGL 1 context.

Custom means composed.

This interface is built from View, Text and Line primitives, with Oxanium and Space Mono defined in separate feature modules. The camera slider supports pointer and keyboard input. Pause, settings, field notes and the mobile site picker use modal focus scopes.

A complete starting point.

The example has its own package.json, Vite configuration, fonts, source files and instructions. The simulation is independent of React and Three.js. Copy it into a new directory and build a game around your own state and art.

Run it as an independent project.

TerminalShell
git clone https://github.com/VictorQueiroz/ludic-canvas.git
cd ludic-canvas/examples/kestrel-survey
npm install
npm run dev

# Production build
npm run build

Node 24+ and a browser with WebGL 2 are required. After copying this example folder elsewhere, the same commands install the published ludic-canvas package. No parent repository files are needed. Its standalone build is verified against the package tarball in CI.

The integration boundary.

Application-owned loop (abridged)TSX
const world = createSurveyWorld(worldCanvas); // Three.js / WebGL 2
const ui = await createUI({
  canvas: overlayCanvas,
  autoStart: false,
  fonts: [displayFont, telemetryFont],
});
ui.render(<SurveyHud game={game} width={width} height={height} onGround={pickGround} />);

function frame(now: number) {
  advanceSurvey(game, deltaSeconds); // Bounded simulation steps on slow frames
  world.draw(game.read(), width, height, deltaSeconds);
  game.publish(world.project()); // World coordinates -> CSS-pixel labels
  ui.frame(now);
  requestAnimationFrame(frame);
}
// Scene teardown: cancel frames, disconnect observers, ui.destroy(), world.dispose().

The complete implementation handles resize, focus, visibility, context recovery, reduced motion and teardown. Three.js is an example dependency and is not added to the Ludic Canvas runtime. Camera projection and gameplay remain application code.

Third-party licenses and font notices