@kognifai/cogsengine
    Preparing search index...

    Cogs.js Manual


    # Requires .npmrc with a valid access token for the private registry
    npm install @kognifai/cogsengine

    # Required peer dependency — math types used throughout the API
    npm install gl-matrix

    # Required if using WebXR, or if TypeScript reports errors in cogs.esm.d.ts
    npm install --save-dev @types/webxr

    # Optional — extra vector utilities and camera helpers
    npm install @kognifai/cogsmatrix

    Recommended imports:

    import * as Cogs from "@kognifai/cogsengine";
    import { dvec3 } from "@kognifai/cogsengine";
    import { vec3, vec4, quat } from "gl-matrix";
    Class Access Purpose
    Control Entry point Owns the canvas, initializes the engine
    Runtime control.runtime Creates entities and components, owns Resources and Scene
    Scene control.runtime.scene Picking, scene origin, entity search, bounding box queries
    Resources control.runtime.resources Loads textures, models and assets; manages resource lifetime
    RuntimeEntity Base class Root type of all scene objects; stored in the engine with GPU-retained state
    Component Base class Unit of functionality attached to an entity

    Commonly used components:

    Component Purpose
    TransformComponent Position, rotation, scale
    SceneComponent Visibility, pickability, child hierarchy
    MaterialComponent Appearance (colour, texture)
    MeshComponent Raw geometry data
    MeshRenderComponent How geometry is rendered (primitive type, render state)
    LodComponent Level-of-detail switching

    Each Cogs instance renders into an HTMLCanvasElement. Set up your HTML layout and pass the canvas to Cogs — do not call WebGL APIs on the canvas yourself.

    See Introduction — Angular based example for a complete integration example. The essential pattern:

    const control = await Cogs.Control.create({
    parent: canvasElement,
    fetchHandler: myFetchHandler,
    onInitialized: () => initScene(),
    });

    const runtime = control.runtime;

    The fetchHandler is a small bridge that lets the Cogs WASM module load its own internal assets (shaders, material definitions, textures) via the browser's network stack. Cogs calls fetch() with a URL, byte offset, and size; your handler performs an XMLHttpRequest with a Range header and returns the result. The canonical implementation from Introduction — Angular based example:

    const fetchHandler: Cogs.IFetchHandler = {
    fetch(url: string, offset: number, size: number,
    handler: Cogs.FetchResponseHandler, fetchId: number): boolean {
    const xhr = new XMLHttpRequest();
    cancellableFetches.set(fetchId, xhr);
    xhr.responseType = "arraybuffer";
    xhr.onloadend = () => {
    cancellableFetches.delete(fetchId);
    handler(
    xhr.status === 200 || xhr.status === 206 ? xhr.response : undefined,
    xhr.status
    );
    };
    xhr.open("GET", url);
    if (size > 0) {
    xhr.setRequestHeader("Range", `bytes=${offset}-${offset + size - 1}`);
    }
    xhr.send();
    return true;
    },
    cancel(fetchId: number): void {
    const xhr = cancellableFetches.get(fetchId);
    if (xhr) xhr.abort();
    },
    };

    cancel is called when Cogs decides a pending fetch is no longer needed (e.g. asset unloaded before download completed). Always implement it to avoid leaking in-flight XHRs.

    Cogs defaults to WebGL2, which is supported by all modern browsers.

    WebGPU is supported as an opt-in alternative to WebGL2. Enable it with:

    await Cogs.Control.create({
    parent: canvas,
    webgpu: true,
    // ...
    });

    WebGPU is available in Chrome and Edge (stable), Firefox (behind a flag), and Safari (experimental, behind a flag). Check runtime.useWebGPU at runtime to confirm which backend is active.

    Cogs uses SharedArrayBuffer for multithreading, which requires cross-origin isolation headers on your server:

    Cross-Origin-Opener-Policy: same-origin
    Cross-Origin-Embedder-Policy: credentialless

    Safari note: credentialless is not supported on Safari. Use require-corp instead, but be aware this restricts which cross-origin resources (images, fonts, scripts) can be loaded without a Cross-Origin-Resource-Policy response header on those resources.

    On startup, check the console for:

    • [TaskMngr][Info] Concurrency: N ... resourceThreads=3 — multithreading is active
    • [Warning][TaskMngr] Multithreaded cogs, but no threading available. — headers are missing or browser is unsupported

    Cogs consists of a TypeScript interface layer and a WebAssembly module. To avoid memory leaks you must explicitly release both.

    control.release();
    canvas.removeEventListener("resize", this.onResize);

    control.release() shuts down rendering and drops all internal references to the WASM module. The module itself can only be garbage-collected after the canvas element is also collected. Dangling event listeners prevent the canvas from being collected — always use named functions so they can be removed:

    // Wrong — lambda cannot be removed:
    canvas.addEventListener("resize", () => this.onResize());

    // Correct:
    canvas.addEventListener("resize", this.onResize);
    // ...on shutdown:
    canvas.removeEventListener("resize", this.onResize);

    Test early: trigger a browser GC (DevTools → Memory → Collect garbage) before and after shutdown and check that memory drops.

    The total WebAssembly memory cap is 2 GB. Browsers may impose stricter limits — iOS devices have been observed to cap at ~384 MB. Test on representative target devices for:

    • Screen resolution and pixel density
    • Memory usage
    • Input devices (mouse, keyboard, touch)

    Every entity is reference-counted. Adding an entity as a child gives the parent an additional reference. You must explicitly destroy entities when done.

    const group = runtime.Group.create();
    const cube = runtime.Cube.create();
    group.sceneComponent.children.add(cube);

    // A) Destroy child first, then parent:
    runtime.destroy(cube); // cube now owned only by parent
    runtime.destroy(group); // both released

    // B) Destroy parent first:
    runtime.destroy(group); // cube reverts to root entity (moved back if parent had a transform)
    runtime.destroy(cube); // release the cube

    Entities can also be created implicitly by loading a scene graph:

    runtime.loadAssetFromString(jsonString);

    // Or via an Asset entity:
    asset.assetComponent.asset = runtime.resources.loadAsset(path);

    Asset-loaded children may be deleted on the next redraw. Do not store references to picked entities — reconstruct from an id instead:

    const entity = runtime.Entity.wrap(id as Cogs.EntityId);
    

    When picking returns the base Entity class, access components via getComponent or by casting:

    function useComponents(entity: Cogs.Entity, runtime: Cogs.Runtime) {
    const tc1 = entity.getComponent(runtime.TransformComponent); // TransformComponent | null
    const tc2 = (entity as any).transformComponent; // TransformComponent | undefined
    const tc3 = (entity as Group).transformComponent; // TransformComponent | undefined
    }

    One component of each type per entity. A component may only belong to one entity at a time.

    const cameraController = runtime.OrbitingCameraController.create();
    camera.components.add(cameraController);
    cameraController.distance = 50;

    // Cleanup:
    camera.components.remove(cameraController);

    Both auto-created and manually added components can be removed:

    // Remove MaterialComponent to use a MeshRenderComponent MaterialInstance instead
    const cube = runtime.Cube.create();
    cube.components.remove(cube.materialComponent);

    Extensions add entity and component types beyond the core. Load an extension before creating any of its entities:

    if (!runtime.loadExtensionModule("Potree")) {
    throw new Error("Potree extension not available in this build");
    }

    const cloud = runtime.PotreeModel.create("PointCloud");

    Note: loadExtensionModule may be deprecated in a future release. IntelliSense shows which extension each entity belongs to.

    See Introduction — Entities and Components for the summary, or Manual — Entity Reference for the full list grouped by extension.

    Custom entities can be defined in the Cogs scene asset JSON format:

    const entityDef = `{
    "templates": {
    "MyGroup": {
    "description": "Custom Group",
    "components": [
    "TransformComponent",
    "SceneComponent",
    "CurvedEarthPositionComponent"
    ]
    }
    }
    }`;

    runtime.Entity.loadDefinitions(entityDef);
    const myEntity = (runtime as any).MyGroup.create() as MyGroup;

    Not all Cogs components and entities are exposed in the TypeScript API. You can register factories for them at runtime.

    Unexposed component:

    // TypeScript declaration for type safety
    declare class CurvedEarthPositionComponent extends Cogs.Component {
    position: vec3;
    }

    declare class MyGroup extends Cogs.Entity {
    get transformComponent(): Cogs.TransformComponent;
    get sceneComponent(): Cogs.SceneComponent;
    get curvedEarthPositionComponent(): CurvedEarthPositionComponent | undefined;
    }

    runtime.Component.createRuntimeFactory("CurvedEarthPositionComponent");

    const earthComp = (runtime as any).CurvedEarthPositionComponent.create() as CurvedEarthPositionComponent;

    Unexposed entity:

    declare class Overlay extends Cogs.Entity {
    get transformComponent(): Cogs.TransformComponent;
    get sceneComponent(): Cogs.SceneComponent;
    get overlayComponent(): OverlayComponent;
    get spriteRenderComponent(): SpriteRenderComponent;
    }

    runtime.Entity.createRuntimeFactory("Overlay");

    const overlay = (runtime as any).Overlay.create("myOverlay") as Overlay;

    Always assign or read the whole field value — never mutate a returned object in place:

    component.field = newValue;       // write
    const value = component.field; // read — treat as read-only

    C++ types map to TypeScript as follows:

    C++ type TypeScript type Source
    float vec2/vec3/vec4/quat/mat4 vec2, vec3, vec4, quat, mat4 gl-matrix
    double vec3 Cogs.dvec3 @kognifai/cogsengine
    double vec2/vec4 Cogs.dvec2, Cogs.dvec4 @kognifai/cogsengine (Float64Array aliases)
    uint32 vec Cogs.uvec2/3/4 @kognifai/cogsengine (Uint32Array aliases)
    int32 vec Cogs.ivec2/3/4 @kognifai/cogsengine (Int32Array aliases)

    Do not use Cogs.vec2, Cogs.vec3, etc. — those are deprecated. Use gl-matrix imports instead.

    Precision trap: JavaScript number is 64-bit double; Cogs fields are 32-bit float. Comparing a value you set against a value you read back can fail:

    const oldPos = vec3.fromValues(1.2345, 2.3456, 3.4567); // Float32Array
    cube.transformComponent.position = oldPos;

    cube.transformComponent.position[0] === 1.2345; // false — float truncation
    cube.transformComponent.position[0] === oldPos[0]; // true — both float

    const oldPosArr: vec3 = [1.2345, 2.3456, 3.4567]; // number[] (double)
    cube.transformComponent.position = oldPosArr;
    cube.transformComponent.position[0] === oldPosArr[0]; // false — float vs double

    // Safest pattern: read from Cogs both times
    const saved = cube.transformComponent.position;
    // ...later:
    cube.transformComponent.position[0] === saved[0]; // true

    Array fields must be set or read as a whole — you cannot modify the returned array:

    // Correct:
    light.lightComponent.cameras = [camera];

    // Wrong — mutation is not detected:
    light.lightComponent.cameras.add(camera);

    Fields that hold arrays of vectors (e.g. Mesh.positions) use typed arrays for efficiency:

    mesh.positions = new Float32Array([
    1, 0, 0,
    1, 1, 0,
    0, 0, 0,
    ]);

    // Compile error in TypeScript, runtime failure in JavaScript:
    mesh.positions = [1, 0, 0, 1, 1, 0, 0, 0, 0];

    Both vec3 and dvec3 accept either a typed array or a plain number array literal:

    camera.transformComponent.position    = vec3.fromValues(1000, 0, 0);
    camera.transformComponent.position = [1000, 0, 0];
    camera.transformComponent.coordinates = dvec3.fromValues(40000, 0, 0);
    camera.transformComponent.coordinates = [40000, 0, 0];

    Some component fields hold a reference to another entity:

    export class DepthAxisComponent extends Component {
    trajectory: Entity | null;
    }

    The field is typed as the base Entity class, but the system expects that entity to have a specific component (here: TrajectoryComponent). Only root entities (not children of a loaded scene) can be used in reference fields.

    Most fields can be read back after being set:

    const camera = runtime.scene.camera;
    const pos = camera.transformComponent.position; // current position

    A few field types cannot be read back if they were set from a loaded scene file rather than by client code:

    • Arrays of entities (except SceneComponent.children)
    • BufferView
    • FontResource
    • TextureResource
    // OK — set and read back by client:
    plane.meshComponent.meshHandle = myMesh;
    const m = plane.meshComponent.meshHandle; // works

    // ERROR — set internally by Cogs, read returns undefined:
    const cameras = headLight.lightComponent.cameras;

    Textures, models and other GPU resources are loaded and tracked via runtime.resources. Resources you create must be explicitly released when no longer needed.

    const texture = runtime.resources.loadTextureViaCogs(imageUrl);
    entity.materialComponent.diffuseMap = texture;

    // Release your handle — the texture stays alive while still in use by the entity
    texture.release();

    // To fully free it, also clear the field and destroy the entity
    entity.materialComponent.diffuseMap = null;
    runtime.destroy(entity);

    The most common leak pattern — loading inline without saving the handle:

    // Leaks — no way to release the texture:
    entity.materialComponent.diffuseMap = runtime.resources.loadTextureViaCogs(imageUrl);

    Always keep a reference so you can release it on cleanup:

    this.texture = runtime.resources.loadTextureViaCogs(imageUrl);
    entity.materialComponent.diffuseMap = this.texture;

    // On cleanup:
    runtime.destroy(entity);
    this.texture.release();
    this.texture = undefined;

    Texture note: TextureResource must not be released until the entity is destroyed. Other resource types (materials, meshes, models) can be released immediately after assigning to a field.

    All resource loading is asynchronous. An entity appears in the scene immediately but renders without its texture/model until loading completes. There is no automatic smooth transition.

    To act when a texture finishes loading, use the textureLoadCallback:

    const control = await Cogs.Control.create({
    // ...
    textureLoadCallback: (id: number, code: number) => {
    if (id === this.texture.id && code === 0) {
    entity.materialComponent.diffuseMap = this.texture;
    }
    },
    });

    // Use ForceUnique to ensure the callback fires even if the URL was loaded before
    this.texture = runtime.resources.loadTextureViaCogs(imageUrl, {
    textureLoadFlags: Cogs.TextureLoadFlags.ForceUnique,
    });

    Entities render using one of two material APIs. They can be combined on the same entity.

    MaterialComponent is the quick path — a component automatically attached to most entities. Set its fields directly and Cogs handles the rest.

    MaterialInstance is a resource you load and configure explicitly. It gives access to the full shader parameter set, blend modes, depth state, and variants. Assign it to meshRenderComponent.material.

    Both can coexist: MaterialComponent fields are applied on top of the MaterialInstance when both are present. To use a pure MaterialInstance without interference, remove the MaterialComponent:

    const cube = runtime.Cube.create();
    cube.components.remove(cube.materialComponent);
    cube.meshRenderComponent.material = myInstance;
    const sphere = runtime.Sphere.create();
    const mc = sphere.materialComponent;

    mc.diffuseColor = vec4.fromValues(0.2, 0.6, 1.0, 1.0); // RGBA, range 0–1
    mc.emissiveColor = vec4.fromValues(0.1, 0.1, 0.1, 1.0);
    mc.transparency = 0.4; // 0 = opaque, 1 = invisible
    mc.diffuseMap = texture; // TextureResource from runtime.resources
    mc.normalMap = normalTex;
    mc.specularMap = specTex;

    // Geometry / render state
    mc.cullMode = Cogs.CullMode.None; // render both sides
    mc.depthWriteEnabled = false; // typical for transparent objects
    mc.drawOrder = 2; // draw after opaque geometry
    mc.enableLighting = false; // unlit
    mc.shadowReceiver = true;

    Loading and assigning:

    const matRes  = runtime.resources.loadMaterial("DefaultMaterial.material");
    const matInst = runtime.resources.loadMaterialInstance(matRes);

    cube.meshRenderComponent.material = matInst;

    // Release your handle when the entity is destroyed
    matInst.release();

    Setting shader properties and textures:

    matInst.setProperty("diffuseColor", vec4.fromValues(1, 0.5, 0, 1));
    matInst.setProperty("roughness", 0.3);
    matInst.setProperty("metallic", 0.8);

    matInst.setTexture("diffuseMap", texture);
    matInst.setTexture("normalMap", normalTex);

    matInst.setTextureAddressMode("diffuseMap", Cogs.AddressMode.Clamp);
    matInst.setTextureFilterMode("diffuseMap", Cogs.FilterMode.MinMagMipLinear);

    Render state options:

    matInst.setOption("CullMode",          "None");   // "Front" | "Back" | "None"
    matInst.setOption("DepthWriteEnabled", false);
    matInst.setOption("DepthTestEnabled", true);
    matInst.setOption("DepthTestAlwaysPass", false);
    matInst.setOption("DrawOrder", 4); // positive = draw later

    Shader variants (select a code path baked into the material):

    skyMaterial.setVariant("Source",    "EnvironmentRadiance");
    skyMaterial.setVariant("StaticLod", "4");
    Name Use
    DefaultMaterial.material Phong shading — diffuseColor, specularColor
    StandardMaterial.material PBR — albedo, roughness, metallic, and matching map textures
    CustomPoints.material Point cloud rendering — pointSize, colorMap

    The active graphics backend is logged at startup: renderer.graphicsDevice = WebGPU, OpenGLES30, or OpenGLES20. Some material variants are only available on specific backends.

    matInst.setOption("Transparency", "On");    // enable alpha blending
    matInst.setOption("BlendMode", "Blend"); // "Blend" | "Add" | "PremultipliedBlend"
    matInst.setOption("DepthWriteEnabled", false); // don't occlude geometry behind
    matInst.setOption("CullMode", "None"); // show back faces too
    matInst.setOption("DrawOrder", 2); // draw after opaques

    Common BlendMode values:

    Value Effect
    "Blend" Standard alpha blend (src·α + dst·(1−α))
    "Add" Additive — bright halos, particles
    "PremultipliedBlend" Premultiplied alpha
    "AlphaToCoverage" MSAA coverage mask — no sort needed
    "None" No blending (opaque)

    For MaterialComponent, set transparency (0–1) and blendMode:

    mc.transparency = 0.5;
    mc.blendMode = Cogs.BlendMode.Blend;
    mc.depthWriteEnabled = false;

    One MaterialInstance can be assigned to many entities — all will render identically:

    const sharedInst = runtime.resources.loadMaterialInstance(matRes);
    sharedInst.setProperty("roughness", 0.2);

    entityA.meshRenderComponent.material = sharedInst;
    entityB.meshRenderComponent.material = sharedInst;
    entityC.meshRenderComponent.material = sharedInst;

    // On cleanup, release once
    sharedInst.release();

    To give each entity its own appearance, create a separate instance per entity:

    for (let i = 0; i < count; i++) {
    const inst = runtime.resources.loadMaterialInstance(matRes);
    inst.setProperty("roughness", i / count);
    entities[i].meshRenderComponent.material = inst;
    }

    Materials and lighting are tightly coupled — a material's appearance depends entirely on the lights in the scene.

    The headlight: Every scene starts with a built-in directional light attached to the camera (runtime.scene.headLight). It always points where the camera looks, so objects are always visible. Disable it when you want full control:

    runtime.scene.headLight.lightComponent.enabled = false;
    

    Light types:

    Type Description Key fields
    Directional Sun-like — parallel rays, no position, infinite range intensity (lux), rotation
    Point Light bulb — radiates in all directions from a position intensity (candela), range
    Spot Torch — a cone from a position in a direction intensity, range, spotAngle
    // Point light — warm white, 100 candela, 20-unit range
    const light = runtime.Light.create("Fill");
    light.lightComponent.lightType = Cogs.LightType.Point;
    light.lightComponent.intensity = 100;
    light.lightComponent.range = 20;
    light.lightComponent.lightColor = vec4.fromValues(1, 0.95, 0.8, 1);
    light.transformComponent.position = vec3.fromValues(0, 5, 5);

    // Directional light — sun at 45° pitch, 30° yaw
    const sun = runtime.Light.create("Sun");
    sun.lightComponent.lightType = Cogs.LightType.Directional;
    sun.lightComponent.intensity = 1.0; // lux
    const dir = quat.create();
    quat.fromEuler(dir, -45, 30, 0);
    sun.transformComponent.rotation = dir;

    How materials react to lights:

    • DefaultMaterial (Phong) — responds to all light types; diffuseColor and specularColor control appearance
    • StandardMaterial (PBR) — physically based; roughness and metallic determine how light scatters
    • Setting materialComponent.enableLighting = false makes an entity render as a flat, unlit colour regardless of scene lights
    • shadowReceiver = true on a material lets it receive shadows cast by lights that have castShadows enabled

    See also Introduction — The 3D world for an introduction to UTM coordinates and the Z-up convention.

    Cogs uses four coordinate spaces, each with a specific purpose and precision:

    Mesh vertex positions before any entity transform is applied. Single-precision (vec3). Keep local coordinates close to the mesh's own origin — precision lost here cannot be recovered downstream.

    In a hierarchy, local coordinates are always relative to the parent entity's transform.

    Double-precision (dvec3) absolute coordinates. This is what you work with in the application.

    JavaScript numbers are 64-bit floats, but the GPU works in 32-bit (vec3). A 32-bit float has only ~7 significant digits of precision. A UTM easting of 598,742.123 already uses 9 digits — so storing it in a vec3 will lose the last two, causing visible jitter or incorrect geometry.

    Cogs solves this with a two-part coordinate system:

    • transformComponent.coordinates (dvec3) — the large world-space offset, stored in 64-bit. Put your UTM values here.
    • transformComponent.position (vec3) — a small local offset from that anchor, stored in 32-bit. Keep this close to zero.
    • scene.origin (dvec3) — a global reference point subtracted before anything goes to the GPU. Set this near the centre of your area of interest.
    GPU position = (entity.coordinatesscene.origin) + entity.position
    [double, stays small] [float, already small]

    Setting up a UTM scene:

    import { dvec3 } from "@kognifai/cogsengine";
    import { vec3 } from "gl-matrix";

    // Pick a reference point near your area of interest (e.g. site centre)
    const siteEasting = 598_700;
    const siteNorthing = 6_638_000;
    const siteAltitude = 0;

    // Tell the engine to subtract this before sending anything to the GPU
    runtime.scene.origin = dvec3.fromValues(siteEasting, siteNorthing, siteAltitude);

    // Place an entity at its full UTM position
    const marker = runtime.Sphere.create("SiteCentre");
    marker.transformComponent.coordinates = dvec3.fromValues(598_742, 6_638_055, 12.5);
    // No need to set .position — it defaults to [0, 0, 0]

    // Place another entity with a small local offset from its UTM anchor
    const sensor = runtime.Cube.create("Sensor");
    sensor.transformComponent.coordinates = dvec3.fromValues(598_700, 6_638_000, 0);
    sensor.transformComponent.position = vec3.fromValues(1.5, 0.3, 0.8); // metres from anchor

    runtime.scene.viewAll();

    World coordinate of a root entity:

    const world = dvec3.add(dvec3.create(), entity.transformComponent.coordinates, entity.transformComponent.position);
    

    Keep the scene origin near the camera target. If the user navigates far from the origin, update it in steps — continuous updates stress shadow rendering.

    Single-precision (vec3) coordinates relative to scene.origin. Used internally by shaders and returned by older Scene API methods. You should not use engine coordinates directly — set scene.origin appropriately and use the World-suffixed picking and bounding-box methods.

    Convert if needed:

    // World → engine
    const engine = dvec3.subtract(dvec3.create(), worldCoord, runtime.scene.origin);

    // Engine → world
    const world = dvec3.add(dvec3.create(), engineCoord, runtime.scene.origin);

    Keep scene.origin near the camera target. Update it in steps — continuous updates stress shadow rendering.

    2D pixel coordinates with origin at the lower-left corner of the canvas. Note: scale by the device pixel ratio when working with high-DPI displays.

    2D UV coordinates, typically in [0, 1]. U maps horizontal, V maps vertical (first row of image = V=0). Behaviour outside [0, 1] is controlled by material.setTextureAddressMode.


    Camera properties — the default camera is runtime.scene.camera. Direct property access:

    const cam = runtime.scene.camera;

    // Field of view (perspective camera)
    cam.cameraComponent.fieldOfView = 60; // degrees

    // Clip planes — geometry outside this range is not rendered
    cam.cameraComponent.nearPlane = 0.1;
    cam.cameraComponent.farPlane = 10000;

    // Switch to orthographic projection
    cam.cameraComponent.projectionType = Cogs.ProjectionType.Orthographic;
    cam.cameraComponent.orthoWidth = 100; // world units wide

    viewAll() — repositions the camera to fit all visible entities in the viewport. Call it after adding objects to the scene:

    runtime.scene.viewAll();
    

    OrbitingCameraController — add this component to the camera to enable mouse/touch orbit, zoom and pan:

    const cameraController = runtime.OrbitingCameraController.create();
    runtime.scene.camera.components.add(cameraController);

    runtime.scene.viewAll();

    Control the camera programmatically:

    cameraController.cameraTarget    = dvec3.fromValues(0, 0, 0);
    cameraController.distance = 20;
    cameraController.horizontalAngle = 0;
    cameraController.verticalAngle = Math.PI / 2;
    cameraController.maxDistance = 10000;

    Utility classes:

    // Focus on a specific entity's bounding box
    const bb = runtime.scene.getBoundingBoxWorld(entity);
    Cogs.CameraUtils.viewAllCamera(runtime.scene.camera, bb, [0, 0]);

    // Extract angles from any orientation quaternion
    const hor = Cogs.CameraHelper.getHorizontalAngle(orientation);
    const vert = Cogs.CameraHelper.getVerticalAngle(orientation);

    Picking is handled via runtime.scene. All recommended methods return world coordinates (methods suffixed World). Avoid older methods without the suffix — they return engine coordinates.

    // Basic: which entity is under the mouse?
    const entity = scene.getPickedEntityWorld(mouseX, mouseY, outWorldCoords);

    // Advanced: full pick info at mouse position
    const info = scene.getPickInfoWorld(mouseX, mouseY);

    // All intersections at mouse position
    const infos = scene.getPickInfoAllWorld(mouseX, mouseY);

    // Pick from a ray (for VR controllers)
    const info = scene.getPickInfoFromRayWorld(rayOrigin, rayDirection);

    Make an entity non-pickable:

    entity.sceneComponent.pickable = false;
    

    By default, all DOM input events on the canvas (pointer, wheel, keyboard, focus) are forwarded to the Cogs engine. The EventFilters interface (Input.ts) lets you intercept any of these events before they reach Cogs — for example to prevent the built-in camera controller from reacting when the pointer is over an HTML overlay element.

    Set filters at any time via the control.eventfilters setter:

    import type { EventFilters, PointerPosition } from "@kognifai/cogsengine";

    control.eventfilters = {
    // Return false to suppress the event — Cogs will not see it
    pointerdown(event: PointerEvent, pos: PointerPosition): boolean {
    // Ignore clicks on overlay UI elements
    const target = document.elementFromPoint(event.clientX, event.clientY);
    return target === control.canvas;
    },

    wheel(event: WheelEvent, pos: PointerPosition): boolean {
    // Suppress scroll when a modal is open
    return !modalIsOpen;
    },

    keydown(event: KeyboardEvent): boolean {
    // Let the browser handle Escape natively
    return event.key !== "Escape";
    },
    };

    All available filter hooks:

    Hook Event type Extra arg
    focus FocusEvent
    blur FocusEvent
    pointerdown PointerEvent PointerPosition
    pointerup PointerEvent PointerPosition
    pointermove PointerEvent PointerPosition
    pointerenter PointerEvent PointerPosition
    pointerleave PointerEvent PointerPosition
    pointercancel PointerEvent PointerPosition
    wheel WheelEvent PointerPosition
    keydown KeyboardEvent
    keyup KeyboardEvent
    contextmenu MouseEvent PointerPosition

    PointerPosition coordinates are already scaled by the device pixel ratio and are relative to the canvas origin (lower-left). All hooks are optional — omit any you do not need.


    Cogs.js supports WebXR for VR and AR experiences. WebXR support is built directly into @kognifai/cogsengine — no separate package is needed.

    The main entry point is Cogs.ControlXR, exported from @kognifai/cogsengine. It manages the XR session lifecycle, reference spaces, virtual position/heading, controller input, and rendering to the headset framebuffer.

    import * as Cogs from "@kognifai/cogsengine";

    const controlXR = new Cogs.ControlXR(control);
    control.setXR(controlXR);

    // Request an immersive VR session
    await controlXR.startXRRequest("immersive-vr", {
    sessionFeatures: { requiredFeatures: ["hand-tracking"] },
    });

    Key ControlXR members:

    Member Purpose
    startXRRequest(mode, options) Request a new XRSession
    virtualPosition (get/set) Viewer position in world space — use for teleporting
    virtualHeading (set) Programmatic heading/yaw in degrees
    xrActiveSession Current XRSession and options

    Event callbacks (passed via options.clientCallbacks: ControlXREventMap):

    Callback Triggered on
    buttonDown / buttonReleased Controller button press/release
    thumbstick / thumbstickDirection Thumbstick movement
    triggerDown / triggerReleased Trigger press/release
    gripPressed / gripReleased Grip press/release
    pinchPressed / pinchReleased Hand-tracking pinch gesture

    Working examples are in examples/js/XRExamples/. The Cogs bridge (Cogs.Bridge) is an internal class that communicates with the WASM module — do not call it from application code.


    Cogs is structured in three layers:

    ┌──────────────────────────────────────────┐
    Cogs.Core
    Engine · Systems · Renderer · Scene
    ├──────────────────────────────────────────┤
    Cogs.Rendering
    Graphics API abstraction
    │ (WebGL · D3D11/12 · Vulkan · WebGPU) │
    ├──────────────────────────────────────────┤
    Cogs.Foundation
    Component model · Memory pools
    Reflection · Collections
    └──────────────────────────────────────────┘

    Each layer only depends on the one below it, keeping the codebase modular and the per-platform adaptation surface small.

    Everything in Cogs is a component stored in a typed pool — a flat, contiguous block of memory holding all instances of one component type, like a tightly-packed array of structs.

    • Cache-friendly iteration. Systems walk linearly through RAM with no pointer chasing. Modern CPUs prefetch this automatically.
    • O(1) allocation. Creating a component never calls malloc. The pool uses a free-list over pre-allocated slots.
    • Safe handles. ComponentHandle = index + generation counter. Stale handles detect slot recycling instead of accessing garbage memory.
    • Parallel data pools. Heavy per-component data (GPU buffers, bounding boxes) lives in a separate ComponentDataPool so the hot data stays compact.

    Every frame, the Engine iterates over all registered Systems in strict priority order:

    PreDynamicComponentsDynamicComponents
    PreTransformTransformPostTransform
    View / Camera
    Geometry / LOD
    Rendering

    Change tracking keeps the loop cheap. Each component carries a bitmask of which fields changed since last frame — the first 24 fields each get their own bit, with an overflow bit for the rest. A system skips a component entirely if no relevant bits are set. Setting a field from TypeScript propagates a changed flag to C++; only what changed gets processed.

    The renderer uses a task-based pipeline:

    1. GenerateListTask — walks visible entities, builds a flat list of RenderItem structs (mesh + material + transform + flags)
    2. FilterListTask — frustum culls, selects LOD level, sorts into render buckets (Backdrop → Solid → Transparent → Overlay)
    3. RenderListTask — submits draw calls via the IGraphicsDevice abstraction; bucket ordering minimises GPU state changes
    4. PostProcessTask — screen-space effects (ambient occlusion, depth of field, shadows, etc.)

    Swapping the backend (e.g. WebGL → WebGPU) requires no change to the pipeline or scene code above it.

    The Cogs engine is written in C++ and compiled to WebAssembly via Emscripten. A flat, C-compatible API (extern "C") in Cogs.Core/Source/Bridge/ exposes engine functionality through opaque handles (BridgeContext, EntityId, ComponentId, FieldId, ResourceId). On the TypeScript side, three layers turn those raw functions into the classes you use in application code:

    Application code

    Public API (Control, Runtime, Scene, Entity, Component, Resources)

    Bridge.ts (typed wrapperresolves EntityEntityId, manages WASM buffers)

    NativeBridge.ts (Emscripten cwrap wrappers — ~260 functions)

    C++ WASM (flat extern "C" functions compiled to WebAssembly)

    Field reads and writes use byte-offset-based accesscube.transformComponent.position = v resolves to a direct memory write at a pre-computed offset inside WebAssembly linear memory, with no runtime reflection lookup. This is as fast as a typed array write.

    The TypeScript wrappers and field offsets are generated automatically by the Cogs code generator from C++ reflection metadata, so the bindings are always in sync with the engine.

    For Cogs engine developers: see Readme-developers.md § Native C Bridge Architecture for the full internal documentation including C API signatures, cwrap patterns, WASM memory management, and the type-generation pipeline.

    Technique Benefit
    Component pools (contiguous arrays) CPU cache locality — no pointer chasing
    O(1) alloc / free without malloc No GC pressure, deterministic timing
    Per-field change bitmasks Systems skip unchanged components entirely
    Render bucket sorting Minimises GPU state changes per frame
    Frustum culling before draw Unseen geometry costs nothing
    LOD system Distant objects use fewer triangles
    WebAssembly Near-native C++ performance in the browser
    Direct memory write for field access No marshalling cost at the JS/WASM boundary

    All entity factories follow the pattern runtime.EntityName.create("optional name"). They return a fully configured entity with all necessary components already attached.

    No loadExtensionModule() call needed.

    Factory Description API
    runtime.Cube.create() Unit cube primitive Cube
    runtime.Sphere.create() Unit sphere primitive Sphere
    runtime.Plane.create() Flat plane (useful as a floor or backdrop) Plane
    runtime.Cylinder.create() Cylinder primitive Cylinder
    runtime.WireCube.create() Wireframe cube (useful for bounding box visualisation) WireCube
    runtime.BasicMeshGeneratorEntity.create() Procedural shape — set meshGeneratorComponent.shape to a Cogs.ShapeType BasicMeshGeneratorEntity
    runtime.GenericShape.create() Custom mesh from raw vertex/index arrays GenericShape
    runtime.MeshPart.create() Generic mesh rendering entity MeshPart
    runtime.MeshPartWithMaterial.create() Mesh with an explicit material component MeshPartWithMaterial
    runtime.LineShape.create() Line rendering (polylines, paths) LineShape
    runtime.Extrusion.create() Extrudes a 2D profile along a Trajectory Extrusion
    runtime.Billboard.create() Flat quad always facing the camera — for labels and icons Billboard
    runtime.ModelEntity.create() Container for a loaded 3D model (GLB, glTF, OpenGEX, etc.) ModelEntity
    runtime.Asset.create() Instantiates a Cogs .asset file (hierarchical model with metadata) Asset
    runtime.Lod.create() Level-of-detail container; switches child entities by camera distance Lod
    runtime.Group.create() Empty transform container for grouping objects in the hierarchy Group
    runtime.Empty.create() Bare entity with no components — attach your own Empty
    runtime.Light.create() Light source — set lightComponent.lightType to Point, Directional, or Spot Light
    runtime.Camera.create() Additional camera (perspective or orthographic) Camera
    runtime.CameraArray.create() Multi-view camera array for VR/stereo rendering CameraArray
    runtime.SkyDome.create() Skybox / environment sphere SkyDome
    runtime.BasicOcean.create() Procedural ocean with waves and reflections BasicOcean
    runtime.BasicTerrain.create() Terrain rendered from elevation and colour data BasicTerrain
    runtime.Trajectory.create() 3D path/well-path data used by Extrusion, Wellbore, etc. Trajectory
    runtime.Wellbore.create() Wellbore visualisation along a Trajectory Wellbore
    runtime.MarkerPointSet.create() Set of 3D marker icons at arbitrary positions MarkerPointSet
    runtime.LookupColorMap.create() Colour lookup map for data-driven colouring LookupColorMap
    runtime.DataSet.create() Generic scalar/vector data container DataSet
    runtime.Environment.get() Global environment settings (ambient light, IBL reflections) Environment
    runtime.Fog.create() Global fog effect Fog

    Each extension must be loaded before any of its entities or components can be created:

    runtime.loadExtensionModule("ExtensionName");
    
    Factory Description API
    runtime.AxisCube.create() Interactive orientation cube with labelled faces (like the viewport gizmo in 3D apps) AxisCube
    runtime.CubeMarker.create() Named marker dot placed on an AxisCube face CubeMarker
    runtime.DepthAxis.create() Depth scale axis for well trajectory visualisations DepthAxis
    Factory Description API
    runtime.BadgeSet.create() Set of 2D icon/number badges rendered at 3D positions BadgeSet
    Factory Description API
    runtime.Casing.create() Casing string visualisation for oil & gas wells Casing
    runtime.BoreHole.create() Open bore hole rendered along a Trajectory BoreHole
    runtime.TrajectoryCylinder.create() Cylinder swept along a Trajectory TrajectoryCylinder
    Factory Description API
    runtime.CurtainView.create() Vertical curtain slice through volumetric data along a path CurtainView
    Factory Description API
    runtime.DrillBit.create() Animated drill bit aligned to a trajectory DrillBit
    runtime.DrillingRiserJoint.create() Standard riser joint segment DrillingRiserJoint
    runtime.DrillingRiserFlexJoint.create() Flexible riser joint at the base of the riser DrillingRiserFlexJoint
    runtime.DrillingRiserTelescopicJoint.create() Telescopic (slip) joint in a riser stack DrillingRiserTelescopicJoint
    runtime.DrillingRiserTensioner.create() Riser tensioner element DrillingRiserTensioner
    runtime.TensionRing.create() Tension ring component for the riser TensionRing
    runtime.RadialLog.create() Radial log data visualised around a wellbore trajectory RadialLog
    Factory Description API
    runtime.HeightMap.create() Terrain surface rendered from a height-map image or data array HeightMap
    runtime.DataSet2D.create() 2D grid dataset — feeds data into HeightMap and similar DataSet2D
    Factory Description API
    runtime.HighlightRegion.create() Highlights a region of the scene with a coloured overlay HighlightRegion
    Factory Description API
    runtime.Image360.create() Equirectangular 360° panorama rendered inside a sphere Image360
    Factory Description API
    runtime.MultiphaseFlow.create() Animated multiphase (oil/gas/water) flow visualisation in a pipe MultiphaseFlow
    Factory Description API
    runtime.OGC3DTiles.create() Streams and renders an OGC 3D Tiles dataset (large city models, point clouds, etc.) OGC3DTiles
    Factory Description API
    runtime.PotreeModel.create() Streams and renders a Potree point cloud dataset PotreeModel
    Factory Description API
    runtime.ProceduralSky.create() Physically-based sky model with sun position and atmospheric scattering ProceduralSky
    Factory Description API
    runtime.Reservoir.create() Reservoir grid (Eclipse/RESQML) with per-cell property colouring Reservoir
    Factory Description API
    runtime.SeaCurrents.create() Animated sea-current vector field visualisation SeaCurrents
    Factory Description API
    runtime.TexAtlas.create() Texture atlas tile system used with large terrain assets TexAtlas
    Factory Description API
    runtime.TwinVisuals.create() Container for digital-twin visual configuration (highlighting, transparency, etc.) TwinVisuals
    runtime.TwinCadModel.create() CAD model optimised for digital-twin use (streaming, tagging, selection) TwinCadModel
    runtime.HighlightRegion.create() Region-based highlight overlay (also available standalone via the HighlightRegion extension) HighlightRegion
    Factory Description API
    runtime.VectorField.create() 3D vector field rendered as animated arrows or streamlines VectorField
    Factory Description API
    runtime.WellLog.create() Well log curves (GR, resistivity, etc.) visualised in 3D along a trajectory WellLog

    Open the built-in debug GUI (frame rate, entity counts, draw calls, resource stats):

    runtime.setVariable("gui.enabled", true);
    

    Log shader source when a shader fails to compile:

    runtime.setVariable("effects.logShaderInfo", true);   // failed shaders only
    runtime.setVariable("effects.logShaderSource", true); // all shaders (verbose)

    These are logged at DEBUG level — enable that level in the browser DevTools console filter.

    Cogs renders on demand by default — a frame is only drawn when something in the scene has changed. Some features (animations, streaming data) enable continuous rendering automatically, but a static scene should not keep the GPU busy.

    Check and control continuous rendering at runtime:

    // Check current state
    console.log(control.continuousRendering); // true = rendering every frame

    // Disable if set unexpectedly
    control.setContinuousRendering(false);

    To verify: open the browser's GPU process monitor (Chrome: chrome://gpu, Task Manager → GPU) and confirm that GPU usage drops when the scene is idle and the camera is not moving.

    If components does not properly check their state the component may for example copy same data to the Mesh at each frame.

    Checking this is best done in a Cogs native code (alternatively temporary logging in Cogs.js or GPU debuggers for browser). Mentioned here as CPU and GPU resources are wasted at each frame.

    Note that a component can be in many states depending on fields and option fields state. Testing not only static setups, but also state transitions an application may apply is important (at least the most used).

    Cogs prints diagnostic messages to the browser console. Control verbosity via a URL parameter when loading your page:

    ?loglevel=0    Traceeverything (very verbose)
    ?loglevel=1 Debug
    ?loglevel=2 Info
    ?loglevel=3 Warning (default)
    ?loglevel=4 Error and Fatal only

    Example: http://localhost:9876/?loglevel=0#myExample

    Key messages to look for on startup:

    Message Meaning
    [TaskMngr][Info] Concurrency: N ... resourceThreads=3 Multithreading active
    [Warning][TaskMngr] Multithreaded cogs, but no threading available. Cross-origin isolation headers missing
    [Info] useWebGPU: true WebGPU backend active

    Cogs holds GPU and WASM heap memory that is not garbage-collected automatically. Improper cleanup causes leaks that accumulate across view mounts and unmounts.

    Quick check with Chrome DevTools:

    1. Mount and unmount your Cogs view (or call control.release() explicitly).
    2. In DevTools → Memory, click Collect garbage two or three times.
    3. Take a heap snapshot.
    4. Memory should be only a few MB. More than 40–50 MB indicates unreleased WASM references.

    Lifecycle stress test (using the Cogs examples):

    http://localhost:9876/lifeCycleTest.html?retries=2&gui&canvas
    
    • Press r to release the current Cogs control instance.
    • Press s to restart the current test.
    • Watch the Memory tab in DevTools for growth across cycles.

    The most common causes of leaks are unreleased resource handles (texture.release(), materialInstance.release()) and entities that are destroyed but whose resource handles are still held by application code. See Resource leaks for ownership rules.

    If developing an example or a demo test that Cogs cleanup works. In example type 'r' to release Cogs instance.

    Can start an example directly using URL. Navigate to example using menu, then copy url to go directly.

    http://localhost:9876/?webgl2&loglevel=0#multiphaseFlowExample

    `Auto play all examples`
    http://localhost:9876/?webgl2&autoplay=4000&loglevel=3

    webgl2 Use the WebGL 2 Cogs Rendering engine.
    autoplay[=msDelay] Enable auto-play with optional delay (neg to disable).
    loglevel=level 0=All, 1=Debug, 1=Info, 3=Warning, 4+=Error+Fatal

    The example gallery (examples/index.html) is the interactive showcase for Cogs.js features. Each example is a self-contained JavaScript module that creates and manipulates a Cogs scene inside a shared host page.

    Each example lives in its own file under examples/js/Examples/. Files are numbered for ordering (e.g. 0002_EntityExample.js) and export one or more named example functions. The registry that maps display names to files and entry-point function names is the demos array at the top of Main.js:

    { name: "Basic Entity Example", file: "0002_EntityExample.js", entry: "entityExample" }
    

    Before the example function is called, Main.js and ExampleRunner together provide a fully initialised environment:

    1. Cogs.Control is created with a WebGL/WebGPU canvas attached to the page, engine configuration (log level, thread counts, pixel ratio, etc.), and all engine callbacks wired up.
    2. ExampleRunner is constructed, giving the example access to control, runtime, scene, and resources. It also registers a mouse-pick handler on the canvas.
    3. runtime.clear() is called to reset all Cogs scene state left over from any previous example run in the same page session.
    4. Camera is reset to a default OrbitingCameraController looking at the origin from a distance of 50 units.

    The example function is then called with (control, runtime, exampleRunner). After it returns, ExampleRunner calls example.initialize() (if provided) and wires up a setInterval at approximately 60 Hz that forwards elapsed time (in seconds) to example.update().

    Navigating to a different example triggers a full page reload — Cogs is torn down completely and restarted fresh for the new example. This means example code does not need to perform full engine teardown. The optional cleanup() callback in ExampleReturn is called during in-page transitions only (e.g. when recompiling via the Source Code panel or during the XR autoplay test suite), not on normal example navigation.

    Install dependencies and start the dev server from the examples/ folder:

    cd examples
    npm ci
    npm run start

    This launches live-server on port 9876 with middleware.js applied. Open http://localhost:9876 in your browser.

    Cogs.js developers who want to test examples against a local build of @kognifai/cogsengine instead of the published package can use npm link:

    # From the repo root — registers the local package globally
    npm link

    # From examples/ — wires the local package into the examples
    cd examples
    npm link @kognifai/cogsengine
    npm run start

    The convenience script start-local does all three steps at once:

    cd examples
    npm run start-local

    To revert to the published package version:

    cd examples
    npm unlink @kognifai/cogsengine
    npm ci

    examples/middleware.js is a live-server middleware that injects HTTP response headers required for SharedArrayBuffer support, which is used by the Cogs WebAssembly threading model:

    • Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp are set on the root page (/).
    • Cross-Origin-Embedder-Policy: require-corp is also set on responses for the Cogs engine JS files served from node_modules/@kognifai/cogsengine/dist/.

    These headers enable cross-origin isolation, which is a browser prerequisite for SharedArrayBuffer and therefore for multi-threaded WebAssembly execution.

    Select an example from the Examples menu in the top navigation bar. The active example is encoded in the URL fragment — for instance:

    http://localhost:9876/#entityExample
    

    Navigate between examples with keyboard shortcuts:

    Key Action
    n or + Next example
    p or - Previous example
    S Restart the current example (useful for checking resource leaks)
    r Cleanup the current example and shut down Cogs (useful for checking memory release)
    g Toggle the debug GUI overlay (keyboard input is disabled while the GUI is shown)
    h Print the list of keyboard shortcuts to the browser console

    The Example Settings menu in the navigation bar provides run-time controls:

    Option Description
    Controls Toggle the draggable Controls panel (shown when the example defines GUI elements)
    Source Code Toggle the draggable Source Code panel, which displays — and lets you live-edit — the running example
    Debug UI Toggle the built-in Cogs debug overlay
    Set pixelratio=0.5 Lower canvas resolution for performance testing
    Set pixelratio=1.0 Restore default canvas resolution

    The Source Code panel lets you modify example code and click Compile to reload it without refreshing the page. Reset Code restores the panel to the original file; Reset Example clears any locally stored edits.

    URL query parameters configure the example host at startup. Combine them as needed:

    http://localhost:9876/?webgl2&loglevel=3#entityExample
    
    Parameter Value Description
    controls (flag) Open the Controls panel on startup
    sourcecode (flag) Open the Source Code panel on startup
    debugui (flag) Enable the Cogs debug overlay on startup
    webgl2 (flag) Use WebGL 2 as the rendering backend (default)
    webgpu (flag) Use WebGPU as the rendering backend
    pixelratio number Set the canvas device pixel ratio (e.g. pixelratio=0.5)
    loglevel 0–4 Minimum log level passed to the Cogs engine: 0=All, 1=Debug, 2=Info, 3=Warning, 4=Error+Fatal
    globalQueueThreadCountFactor number Fraction of logical CPU cores allocated to the global worker queue (e.g. 0.3)
    globalQueueThreadCountMax number Hard ceiling on global worker queue thread count
    resourceQueueThreadCountFactor number Fraction of logical CPU cores allocated to the resource-loading queue
    resourceQueueThreadCountMax number Hard ceiling on resource-loading queue thread count
    stutter (flag) Batch all engine responses and flush them once per second — stress-tests response-handling code

    Example — disable threading (useful when debugging or profiling single-threaded behaviour):

    http://localhost:9876/?resourceQueueThreadCountMax=0#entityExample
    

    Each Cogs.js example can define a custom Controls panel that lets the user interact with the example at runtime. The Controls panel has a predefined set of supported element types, parsed and rendered by index.html.

    For an example to have a Controls panel, define an array of GUI entry objects and return it as gui from the example function:

    // See complete example below for correct import + TypeScript defs.
    const myExampleGUI = [/*gui specification here*/];

    export function someExample(control, runtime, exampleRunner)
    {
    /* example creation code here */
    return {
    initialize: initFunc, /* optional: ran after creation */
    gui: myExampleGUI, /* optional: must be returned if gui wanted */
    update: function (t) { /* optional: called at 60 Hz. t = seconds since last call */
    /* example update callback */
    },
    cleanup: cleanupFunc /* optional: ran on cleanup */
    };
    }

    Each entry in the array must have at least three properties: type, name, and callback.

    • type — the element kind (see table below)
    • name — label or description shown in the Controls panel
    • callback — function called when the element is interacted with
    // @ts-check
    import * as Cogs from "@kognifai/cogsengine";
    import { mat4, quat, vec2, vec3, vec4 } from "gl-matrix";
    import { dvec3 } from "@kognifai/cogsengine";

    /**
    * Skeleton example. Scales a Cube using Example GUI
    * Add Example to: examples\index.html:
    * { name: "Some Example", file: "9991_someExample.js", entry: "someExample" },
    */

    let someSizeChanged = false;
    let someSize = 1;

    /** @type {import("../ExampleRunner/ExampleRunner").ExampleGuiEntry[]} */
    const myExampleGUI = [
    {
    type: "slider",
    name: "Change some size",
    min: 1,
    max: 2000,
    value: someSize,
    callback: function (e) {
    someSizeChanged = true;
    someSize = e.target.value;
    },
    },
    ];

    /**
    * @param {Cogs.Control} control
    * @param {Cogs.Runtime} runtime
    * @param {import("../ExampleRunner/ExampleRunner").ExampleRunner} exampleRunner
    * @returns {import("../ExampleRunner/ExampleRunner").ExampleReturn|void}
    */
    export function someExample(control, runtime, exampleRunner) {
    const cube = runtime.Cube.create("SomeCube");
    return {
    gui: myExampleGUI,
    update: function (t) {
    if (someSizeChanged) {
    someSizeChanged = false;
    cube.transformComponent.scale = vec3.fromValues(someSize, someSize, someSize);
    }
    },
    };
    }

    Note: The update function can be omitted if the GUI callbacks update Cogs state directly.

    type Description name Extra properties callback
    button A clickable button Displayed inside the button No params
    slider A draggable slider Used as label and id min (number), max (number), value (number, start value) e.target.value = current slider value
    check A checkbox Used as label and id checked (boolean, default state) checked (boolean) = current state
    select A dropdown menu Used as label items (array of {text, value} objects) e.target.value = selected item value
    divider A horizontal rule separator Not used Not used