Flight is a modular TypeScript SDK for building interactive applications — designed to be read and written by AI coding agents as well as people. Its 149 tree-shakable packages span 2D and 3D graphics, physics, animation, media, input, asset formats, application lifecycle, and desktop/mobile platform APIs.
The API is cellular: explicit passes over plain data, self-identifying names, clear ownership, and no work at import time. A function can be understood in isolation; an application can import one capability without inheriting an engine. Those same properties keep the codebase grepable for agents, portable to compiled targets, and small in production builds.
Build a scene once and choose a renderer — Canvas 2D, DOM, WebGL 2, or WebGPU — by registering only the implementations it uses. Compose the same small primitives into a game, visualization, media experience, desktop tool, or mobile application.
See Flight render its own live introduction, browse the examples gallery, compare ports from established frameworks, or run the gallery locally:
npm install
npm run examplesnpm install @flighthq/sdkCreate a display-object scene, register the renderer kinds you use, update the render graph, then draw:
import {
addNodeChild,
createCanvasElement,
createCanvasRenderState,
createDisplayObject,
createSprite,
createTexture,
defaultCanvasSpriteRenderer,
getCanvasRenderStateTextureResolvers,
loadImageResourceFromUrl,
prepareScene2DRender,
registerCanvasImageTextureResolver,
registerRenderer,
renderCanvasBackground,
renderCanvasScene2D,
SpriteKind,
} from '@flighthq/sdk';
const pixelRatio = window.devicePixelRatio || 1;
const canvas = createCanvasElement(550, 400, pixelRatio);
document.body.appendChild(canvas);
const state = createCanvasRenderState(canvas, {
backgroundColor: 0xeeddccff,
sceneGraphSyncPolicy: 'requiresInvalidation',
});
registerCanvasImageTextureResolver(getCanvasRenderStateTextureResolvers(state));
registerRenderer(state, SpriteKind, defaultCanvasSpriteRenderer);
const root = createDisplayObject();
root.scaleX = pixelRatio;
root.scaleY = pixelRatio;
const sprite = createSprite();
const image = await loadImageResourceFromUrl('assets/wabbit_alpha.png');
sprite.data.texture = createTexture({ dimension: '2d', source: image });
addNodeChild(root, sprite);
function enterFrame(): void {
if (prepareScene2DRender(state, root)) {
renderCanvasBackground(state);
renderCanvasScene2D(state, root);
}
requestAnimationFrame(enterFrame);
}
enterFrame();The same scene renders through DOM, WebGL2, or WebGPU by creating the matching render state and registering that backend's renderers — no change to the scene graph itself. The display objects stay plain data; backend work remains explicit.
The application package provides a request-animation-frame loop with typed update and render signals:
import {
connectSignal,
createApplication,
createTween,
createTweenManager,
easeOutElastic,
invalidateNodeRender,
startApplicationLoop,
updateTweens,
} from '@flighthq/sdk';
const manager = createTweenManager();
const tween = createTween(manager, sprite, 1000, { x: 400, alpha: 0 }, { ease: easeOutElastic });
connectSignal(tween.onUpdate, () => invalidateNodeRender(sprite));
const app = createApplication();
connectSignal(app.onUpdate, (delta) => updateTweens(manager, delta));
connectSignal(app.onRender, () => {
if (prepareScene2DRender(state, root)) {
renderCanvasBackground(state);
renderCanvasScene2D(state, root);
}
});
startApplicationLoop(app);Wire up pointer events on any scene node. Register a hit-test strategy once, then create an interaction manager and connect it to the input system:
import {
attachPointerInput,
connectInputToInteraction,
connectInteractionSignal,
createInputManager,
createInteractionManager,
invalidateNodeRender,
registerDefaultHitTests,
setNodeHitTestEnabled,
} from '@flighthq/sdk';
registerDefaultHitTests();
setNodeHitTestEnabled(sprite, true);
const interaction = createInteractionManager(root);
const input = createInputManager();
attachPointerInput(input, canvas);
connectInputToInteraction(input, interaction, pixelRatio);
connectInteractionSignal(interaction, sprite, 'onPointerDown', () => {
sprite.alpha = 0.5;
invalidateNodeRender(sprite);
});
connectInteractionSignal(interaction, sprite, 'onPointerUp', () => {
sprite.alpha = 1;
invalidateNodeRender(sprite);
});Load an audio resource with fallback formats, then play it:
import { loadAudioResourceFromUrls, playAudioResource } from '@flighthq/sdk';
const audioContext = new AudioContext();
const sound = await loadAudioResourceFromUrls(audioContext, [{ url: 'assets/click.ogg' }, { url: 'assets/click.mp3' }]);
connectInteractionSignal(interaction, sprite, 'onPointerDown', () => {
playAudioResource(audioContext, sound);
});Flight separates authored data from backend work:
- Build a graph from display objects, sprites, text, shapes, tilemaps, particles, or 3D scene nodes.
- Create a backend render state for Canvas 2D, DOM, WebGL2, or WebGPU.
- Register only the renderers and effect backends the scene needs.
- Run the explicit prepare/update pass.
- Draw through the selected backend.
Canvas and DOM are lightweight host-web paths. WebGL2 and WebGPU add GPU render targets, cached pipelines, shader/material registries, post-processing, clipping, masking, velocity, render caches, 2D batching, and 3D forward renderers with a 20-material Cook-Torrance PBR + classic/NPR/debug shader library.
Flight currently spans 149 independently importable, publishable packages. The public API can be inspected with:
npm run api
npm run api -- --jsonMajor areas:
| Area | Packages |
|---|---|
| Core and data | types, entity, node, signals, math, geometry, color, layout, compression |
| 2D graphics | scene2d, bitmap, shape, path, clip, text, tilemap, quadbatch |
| 3D graphics | scene3d, mesh, materials, shading, lighting, texture, camera, skeleton3d, picking |
| Rendering | render, Canvas/DOM scene renderers, WebGL 2 and WebGPU cores, and 2D/3D GPU renderers |
| Effects and imaging | adjustments, effects, Canvas/WebGL/WebGPU effect executors, image, image-codec, capture |
| Text | textlayout, textshaper, textsegment, textbidi, textinput, glyphatlas, bitmapfont, bitmaptext, text-markup |
| Animation and simulation | easing, tween, spring, animation, timeline, movieclip, spritesheet, motionpath, clock, particles |
| Games and interaction | input, interaction, collision, physics2d, spatial, flow, statechart, snapshot |
| Resources and formats | Loaders and structured importers for images, fonts, atlases, tilemaps, particles, SVG, SWF, glTF, OBJ/MTL, 3DS, MD2/MD5, AWD2, and more |
| Application and media | application, audio, video, media, mediasession, intl, log, debug |
| Platform integration | Web-first APIs for storage, networking, filesystem, clipboard, dialogs, notifications, sensors, windows, lifecycle, and other OS/device capabilities |
| Native hosts | Replaceable adapters for Electron, Tauri, and Capacitor |
| Tooling | Capture and baseline tooling, cross-renderer smoke/parity checks, conformance fixtures, API/export/package/order/portability gates, and bundle-size budgets |
All packages are published under @flighthq/. Applications and examples usually import from @flighthq/sdk:
import { addNodeChild, createBitmap, createShape } from '@flighthq/sdk';Library code should prefer the smallest package root that provides the needed API:
import { createTween, updateTweens } from '@flighthq/tween';Web implementations are available by default for the platform layer. Native hosts replace them through explicit backends, keeping application code stable across browser, desktop, and mobile targets.
Flight is pre-release software, so confidence comes from the engineering path behind each change. Code changes are exercised by two unit-test lanes, a full TypeScript build, package and public-API validation, bundle-size budgets, and browser-render smoke and parity checks across DOM, Canvas, WebGL, and WebGPU. Nightly runs add coverage thresholds and generated API-surface checks.
main is a machine-managed known-good pointer: it advances only to commits whose CI run succeeded. Version releases verify that exact tagged commit passed CI before publishing the complete package graph to npm with provenance.
The repository also keeps reliability concerns visible in the design: imports are side-effect-free, feature families use explicit registries, resource ownership and allocation are named, format importers report structured diagnostics, source stays inside a checked compiled-target-portable subset, and render output is tested across interchangeable backends.
The gallery includes focused examples for shapes, bitmaps, text, interaction, animation, tilemaps, particles, collision, application flow, audio/video, file-format loading, and a growing set of 3D scenes covering primitives, shading, skyboxes, skeletal animation, picking, fire, explosions, and a globe. Where a capability is shared, the gallery lets you switch the same scene among supported renderers.
Build a specific example:
npm run build --workspace=examples/packages/shapesRun a renderer-specific example dev server:
npm run dev:canvas --workspace=examples/packages/shapes
npm run dev:dom --workspace=examples/packages/shapes
npm run dev:webgl --workspace=examples/packages/shapes
npm run dev:webgpu --workspace=examples/packages/scene-primitivesgit clone https://github.com/flighthq/flight.git
cd flight
npm installpackages/ Workspace packages published as @flighthq/*
examples/ Standalone Vite example apps
conformance/ Import fixtures and structured conformance checks
tools/ Gallery, functional scenes, capture harness, and repo utilities
scripts/ Validation, API, coverage, ordering, size, and build scripts
Useful checks:
npm run check # package, type, API, export, portability, docs, and style gates
npm run test # unit tests across all packages
npm run size # gzip output size against committed budgets
npm run test:smoke # fail on browser/runtime/render errors
npm run test:parity # compare output across render backends
npm run test:regression # compare output against committed fingerprints