Skip to content

@carrot/engine ​

ts

Umbrella orchestrator for the Carrot WebGL2 engine - owns the game loop, timing, input, renderer, and shader registry.

Installation ​

bash
npm install @carrot/engine

Architecture / How It Works ​

Engine is deliberately thin. It creates and wires up the core subsystems, then runs a requestAnimationFrame loop that dispatches signals at each phase:

  1. Timing - EngineTime.update() computes delta, smoothed delta, FPS, and advances the fixed-timestep accumulator.
  2. Input - Input.update(delta) polls keyboard/mouse/touch state.
  3. Update - The game's update callback runs with the current delta.
  4. Render - Post-render signal fires (the game submits draw calls during its update).

Signals (onLoopStart, onPreInput, onPostInput, onPreUpdate, onPostUpdate, onPostRender, onResize) let systems hook into the loop without subclassing.

DisplayInfo handles DPI-aware coordinate conversion - logical (CSS pixels) vs physical (device pixels), plus real-world unit conversion (cm, inches).

DPR-Transparent Coordinate System ​

The engine provides a DPR-transparent coordinate system. engine.width and engine.height always return logical CSS pixels - game code never needs to think about device pixel ratio. Under the hood, resizeCanvas() scales the canvas backing store by devicePixelRatio for sharp rendering on HiDPI displays, and rebuilds a DisplayInfo on each resize. The initial resize happens during construction so width, height, and display are valid immediately after new Engine(canvas).

EngineTime._last and _now initialise to performance.now() (not 0), which prevents a spurious large delta on the first frame.

Dependencies ​

PackageUsed For
@carrot/signalsEvent signals for game loop phases
@carrot/loggingOptional structured logging
@carrot/inputKeyboard, mouse, touch input
@carrot/engine-rendererWebGL2 renderer
@carrot/engine-renderer-shadersShader program registry
@carrot/maths-geometrySize2, Vector2 used by DisplayInfo

Build ​

bash
npm run build   # runs tsc

Using @carrot/engine ​

Umbrella orchestrator for the Carrot WebGL2 engine - initialise, start the loop, hook into signals.

Import ​

ts
import { Engine, EngineTime, DisplayInfo } from '@carrot/engine';

Common Patterns ​

1. Basic game loop ​

ts
const canvas = document.getElementById('game') as HTMLCanvasElement;
const engine = new Engine(canvas);

engine.start((delta) => {
  // game logic
  // submit draw calls via engine.renderer
});

2. Hooking into loop phases ​

ts
engine.onPreUpdate.add(() => {
  // runs before the game update callback every frame
});

engine.onPostRender.add(() => {
  // runs after all rendering - good for debug overlays
});

engine.onResize.add(() => {
  // canvas resized - update cameras, UI layout, etc.
});

3. Fixed-timestep physics ​

ts
engine.start((delta) => {
  while (engine.time.shouldStepPhysics) {
    physicsWorld.step(1 / 60);
    engine.time.stepPhysics();
  }

  renderScene();
});

4. Reading frame stats ​

ts
engine.onPostRender.add(() => {
  hud.setText(`FPS: ${engine.time.fps}`);
  hud.setText(`Delta: ${engine.time.smoothedDelta.toFixed(4)}s`);
  hud.setText(`Total: ${engine.time.totalTime.toFixed(1)}s`);
});

5. Using logical canvas dimensions ​

ts
// engine.width and engine.height are always in logical CSS pixels -
// you never need to care about DPR when positioning game objects.
const halfW = engine.width / 2;
const halfH = engine.height / 2;

// The full DisplayInfo is available if you need physical pixels or DPI:
const physicalPos = engine.display.toPhysical(mousePosition);
const borderPx = engine.display.cmToPixels(0.5);

6. Stopping the engine ​

ts
engine.stop();
// The loop stops after the current frame completes

Package Contents ​

@carrot/engine-buffers ​

Import: import { FloatBuffer, IndexBuffer } from '@carrot/engine-buffers';

Classes ​

ClassDescription
FloatBufferCPU-side Float32Array wrapper for vertex attribute data
IndexBufferCPU-side index buffer - auto-selects Uint16Array or Uint32Array

FloatBuffer ​

Constructor: new FloatBuffer(sizeOrData: number | number[] | Float32Array)

MemberTypeDescription
dataFloat32ArrayRaw typed array
lengthnumberElement count
byteLengthnumberSize in bytes

Methods: get(index) · set(index, value) · set(values) · setAt(offset, values) · fill(value, start?, end?) · slice(start?, end?) · clone() · toArray() · copyFrom(source, srcOffset?, destOffset?, length?)

Iteration: Implements [Symbol.iterator] for for...of loops.

IndexBuffer ​

Constructor: new IndexBuffer(data: number[] | Uint16Array | Uint32Array)

MemberTypeDescription
dataUint16Array | Uint32ArrayRaw typed array (auto-selected)
lengthnumberElement count
byteLengthnumberSize in bytes
is32BitbooleanWhether using Uint32Array (max index > 65535)

Methods: get(index) · set(index, value) · slice(start?, end?) · clone() · toArray()

Iteration: Implements [Symbol.iterator] for for...of loops.


@carrot/engine-textures ​

Import: import { Texture2D, DataTexture, Texture3D, TextureCube, RenderTexture, ... } from '@carrot/engine-textures';

Classes ​

ClassDescription
TextureAbstract GPU texture base - handle, descriptor, bind/unbind/dispose lifecycle
Texture2D2D texture loaded from an AssetImage
DataTexture2D texture from raw typed array data (noise, LUTs, SDF atlases)
Texture3D3D volumetric texture (voxel grids, 3D LUTs)
TextureCubeCubemap texture - 6 faces (skyboxes, environment reflections)
RenderTextureFramebuffer-backed render target with optional depth
TextureBuilder2DFluent builder for procedural 2D data textures
TextureBuilder3DFluent builder for procedural 3D data textures

Enums / Constants ​

NameValues
TextureTargetTexture2D · Texture3D · TextureCube
TextureFormatRGBA8 · RGB8 · RG8 · R8 · RGBA16F · RGB16F · RG16F · R16F · RGBA32F · RGB32F · RG32F · R32F · Depth16 · Depth24 · Depth32F · Depth24Stencil8
TextureFilterNearest · Linear · NearestMipmapNearest · LinearMipmapNearest · NearestMipmapLinear · LinearMipmapLinear
TextureWrapClamp · Repeat · Mirror
CubeFacePositiveX · NegativeX · PositiveY · NegativeY · PositiveZ · NegativeZ

Types ​

TypeDescription
TextureDescriptorImmutable config - format, filtering, wrapping, mipmaps
TextureDataSourceFloat32Array | Uint8Array | Uint16Array | null
Texture3DDataSourceFloat32Array | Uint8Array | null
TexelFn2D(x: number, y: number) => number - per-texel callback
TexelFn3D(x: number, y: number, z: number) => number - per-texel callback

Descriptor Presets ​

defaultTextureDescriptor · defaultRenderTextureDescriptor · pixelArtTextureDescriptor

Factory Functions ​

FunctionReturnsDescription
noiseTexture2D(gl, w, h, noise, scale?, offsetX?, offsetY?, desc?)DataTextureGenerate a 2D R32F noise texture
noiseTexture3D(gl, w, h, d, noise, scale?, desc?)Texture3DGenerate a 3D R32F noise texture

Texture (abstract base) ​

MemberType
glWebGL2RenderingContext
descriptorTextureDescriptor
targetTextureTarget
handleWebGLTexture | null
isUploadedboolean
width · height · depthnumber

Methods: bind(unit?) · unbind(unit?) · dispose() · upload() (abstract)

RenderTexture (additional) ​

MemberType
framebufferWebGLFramebuffer | null

Methods: resize(width, height) · bindAsTarget() · unbindTarget() · dispose()

TextureBuilder2D ​

Factory: TextureBuilder2D.create(gl, width, height)

Methods (fluent): set(fn) · add(fn, weight?) · multiply(fn) · curve(fn) · remap(inMin, inMax, outMin, outMax) · clamp(min?, max?) · normalise() · descriptor(desc) · build()

TextureBuilder3D ​

Factory: TextureBuilder3D.create(gl, width, height, depth)

Methods (fluent): set(fn) · add(fn, weight?) · multiply(fn) · curve(fn) · remap(inMin, inMax, outMin, outMax) · clamp(min?, max?) · normalise() · descriptor(desc) · build()


@carrot/engine-shaders ​

Import: import { ShaderSource, ShaderFile, ShaderUniform, ShaderUniformType } from '@carrot/engine-shaders';

Classes ​

ClassDescription
ShaderSourceComplete shader description - id, GLSL version, precision, vertex/fragment source, includes, uniforms
ShaderFileParses a .shader XML file into a ShaderSource
ShaderUniformMetadata for a single shader uniform - name, GLSL type, optional default

Enums / Constants ​

NameValues
ShaderUniformTypeFloat · Int · Bool · Vec2 · Vec3 · Vec4 · Mat3 · Mat4 · Sampler2D · SamplerCube

ShaderSource ​

Constructor: new ShaderSource(id, version, precision, vertexRaw, fragmentRaw, includes?, uniforms?)

MemberType
idstring
versionstring
precisionstring
vertexRawstring
fragmentRawstring
includesreadonly string[]
uniformsreadonly ShaderUniform[]
uniformNamesReadonlySet<string>

Methods: renderVertex() · renderFragment() · getUniform(name) · toString()

ShaderFile ​

Constructor: new ShaderFile(rawXml: string)

MemberType
sourceShaderSource
id / keystring
versionstring
precisionstring
vertexRaw · fragmentRawstring
includesreadonly string[]
uniformsreadonly ShaderUniform[]

Methods: renderVertex() · renderFragment()

ShaderUniform ​

Constructor: new ShaderUniform(name, type, defaultValue?)

MemberType
namestring
typeShaderUniformType
defaultValuestring | undefined

Methods: fromXml(xml) (static) · toString()


@carrot/engine-materials ​

Import: import { Material, MaterialInstance, MaterialSchema, ... } from '@carrot/engine-materials';

Classes ​

ClassDescription
MaterialAbstract typed property bag - references a shader by key, defines properties via schema
MaterialInstancePer-object runtime copy with property overrides and dirty tracking
MaterialSchemaOrdered collection of property definitions for a material
MaterialPropertyDef<T>Typed property definition - key, label, type, default, constraints
Material2DSpriteStandard 2D sprite material (unlit, textured + tint)
Material2DUnlitColorFlat colour material (unlit, no texture)
Material2DUnlitTexturedGeneral-purpose unlit textured material (texture + colour)

Enums / Constants ​

NameValues
MaterialPropertyTypeColor · Texture · Number · Boolean · Vector2 · Vector3 · Vector4 · Enum
MaterialTypeUnlit · Simple · Pbr

Factory Functions ​

FunctionDescription
defineProperty<T>(key, label, propertyType, config?)Create a MaterialPropertyDef<T>

Material (abstract base) ​

MemberType
namestring
shaderKeystring
schemaMaterialSchema
typeMaterialType (abstract)
transparentboolean (get/set)

Methods: getProperty(def) · setProperty(def, value) · getPropertyByKey(key) · setPropertyByKey(key, value) · getPropertyEntries()

MaterialInstance ​

MemberType
materialTMaterial
namestring (get/set)
typeMaterialType
schemaMaterialSchema
shaderKeystring (get/set, overrideable)
transparentboolean
isDirtyboolean

Methods: getProperty(def) · setProperty(def, value) · clearProperty(def) · hasOverride(def) · getPropertyByKey(key) · setPropertyByKey(key, value) · resetOverrides() · markDirty() · clearDirty()

MaterialSchema ​

Methods: define(def) · get(key) · has(key) · getAll() · size

Iteration: Implements [Symbol.iterator].

MaterialPropertyDef<T> ​

MemberType
keystring
labelstring
propertyTypeMaterialPropertyType
defaultValueT | undefined
optionsreadonly T[] | undefined
min · max · stepnumber | undefined

Built-in Materials ​

MaterialShader KeyProperties
Material2DSpritecore.sprite.defaultmainTexture (Texture) · tintColor (Color)
Material2DUnlitColorcore.unlit.colorcolor (Color)
Material2DUnlitTexturedcore.unlit.texturedmainTexture (Texture) · color (Color)

@carrot/engine-meshes ​

Import: import { Mesh, VertexView, meshFromQuad, meshFromCircle, ... } from '@carrot/engine-meshes';

Classes ​

ClassDescription
MeshCPU-side mesh - named bag of vertex attribute buffers and triangle indices
VertexViewZero-allocation view into a single vertex's attributes (used by forEachVertex)

Types ​

TypeDescription
MeshDataConstruction data - all attribute buffers optional

Primitive Generators ​

FunctionDescription
meshFromQuad(width, height?, name?)2D quad in XY plane, centered at origin
meshFromTriangle(a, b, c, name?)2D triangle in XY plane from 3 points
meshFromCircle(radius, segments?, name?)Circle/disc in XY plane, triangle fan
meshFromPlane(width, depth, subdivisionsX?, subdivisionsZ?, name?)Subdivided plane in XZ plane (Y up)
meshFromCube(size?, name?)Unit cube, 24 verts (hard edges), 12 tris

Path-to-Mesh Generators ​

FunctionDescription
meshFromStrip(points, width, name?)Quad strip ribbon from a polyline
meshFromLoop(points, width, name?)Closed quad strip from a polyline loop
meshFromBezier(bezier, width, samples?, name?)Quad strip along a single bezier curve
meshFromBezierStrip(strip, width, samples?, name?)Quad strip along a bezier strip path
meshFromBezierLoop(loop, width, samples?, name?)Closed quad strip along a bezier loop

Mesh ​

Constructor: new Mesh(name: string, data?: MeshData)

MemberTypeDescription
namestringMesh name
uniqueIdnumberAuto-incrementing ID
positionsFloatBuffer?Vertex positions (vec3)
normalsFloatBuffer?Vertex normals (vec3)
tangentsFloatBuffer?Vertex tangents (vec4)
uv1 ... uv6FloatBuffer?UV channels (vec2)
colorsFloatBuffer?Vertex colors (vec4)
indicesIndexBuffer?Triangle indices
isDirtybooleanDirty flag for renderer
vertexCountnumberDerived from positions buffer
triangleCountnumberDerived from index buffer

Methods: setColorsAll(color) · computeNormals() · forEachVertex(fn) · clone(name?) · markDirty() · clearDirty() · toString()

VertexView ​

Per-vertex accessor - reads/writes directly into underlying FloatBuffers.

Position: px · py · pzNormal: nx · ny · nzUV1-UV6: u1/v1 · u2/v2 · u3/v3 · u4/v4 · u5/v5 · u6/v6Color: cr · cg · cb · ca

MeshData ​

FieldType
positions · normals · tangentsFloatBuffer?
uv1 ... uv6FloatBuffer?
colorsFloatBuffer?
indicesIndexBuffer?

Carrot