Who this is for: TypeScript/JavaScript developers who want to display 3D content in the browser but have no prior experience with 3D graphics engines.
No prior 3D graphics knowledge is required.
Cogs uses a right-handed, Z-up world coordinate system:
| Axis | Direction | Geographic meaning |
|---|---|---|
| X | East | Easting |
| Y | North | Northing |
| Z | Up | Altitude / elevation |
This matches the UTM (Universal Transverse Mercator) coordinate system directly — no manual axis remapping is needed. Distances have no fixed real-world unit; if you work in UTM the natural unit is metres.
OpenGL vs Cogs: OpenGL's rendering pipeline is Y-up, but Cogs uses Z-up in world space. Cogs handles the internal rotation between these automatically — you always work in Z-up world coordinates.
The most common vector types you'll encounter are:
| Type | What it is | Example |
|---|---|---|
vec3 |
3D position or direction (single precision) | vec3.fromValues(1, 2, 3) |
dvec3 |
Double-precision 3D position — for large real-world coordinates | dvec3.fromValues(600000, 7000000, 0) |
vec4 |
Colour (R, G, B, A) or homogeneous position | vec4.fromValues(1, 0, 0, 1) — red |
quat |
Rotation (quaternion — think of it as an angle+axis) | quat.create() — no rotation |
All colour values are in the range 0–1, not 0–255.
For a full explanation of vec3 vs dvec3 precision, UTM setup, and the two-part coordinate system, see Manual — World coordinates.
An entity is any object in your scene — a cube, a 3D model, a light, the camera. By itself an entity does nothing; it is just a container.
Components are the building blocks that give an entity behaviour and appearance. Every entity has a set of components attached to it. You read and write component fields (properties) to control the entity.
Entity "MyCube"
├── transformComponent → position, rotation, scale
├── materialComponent → colour, texture
├── meshRenderComponent → which mesh to render
└── sceneComponent → visibility, parent/child
You never construct these components yourself. When you create an entity via runtime.Cube.create(), all relevant components are created and attached for you. You just set their fields:
const cube = runtime.Cube.create();
cube.transformComponent.position = vec3.fromValues(0, 2, 0); // move up 2 units
All entity factories follow the pattern runtime.EntityName.create("optional name"). The built-in entities available out of the box are:
Core (no loadExtensionModule needed):
| Category | Factories |
|---|---|
| Primitives | Cube, Sphere, Plane, Cylinder, WireCube |
| Mesh / model | GenericShape, MeshPart, LineShape, Extrusion, Billboard, ModelEntity, Asset |
| Organisation | Group, Empty, Lod |
| Lighting / sky | Light, SkyDome, BasicOcean, BasicTerrain, Fog, Environment |
| Camera | Camera, CameraArray |
| Data / paths | Trajectory, Wellbore, MarkerPointSet, DataSet, LookupColorMap |
Extensions (each requires runtime.loadExtensionModule("Name")):
AxisCube · BadgeSet · Casing · CurtainView · Drilling · HeightMap · HighlightRegion · Image360 · MultiphaseFlow · OGC3DTiles · Potree · ProceduralSky · Reservoir · SeaCurrents · TexAtlas · TwinVisuals · VectorField · WellLog
For the full component lifecycle, custom entities, and advanced field access, see Manual — Entities and Components and Manual — Entity Reference.
A material defines how the surface of an object looks — its colour, shininess, and how it responds to light.
A texture is an image (PNG, JPG, etc.) that is "wrapped" onto a surface. You can think of it like shrink-wrap: the image is stretched to cover the shape.
The simplest way to colour an object is through its materialComponent.diffuseColor:
import { vec4 } from "gl-matrix";
cube.materialComponent.diffuseColor = vec4.fromValues(1, 0.2, 0.2, 1); // red
For more realistic appearance — shiny metals, rough stone, etc. — you use a StandardMaterial with Physically-Based Rendering (PBR) properties:
| Property | What it controls | Range |
|---|---|---|
albedo |
Base colour | vec4 (RGBA 0–1) |
roughness |
How rough/smooth the surface is (0 = mirror, 1 = chalk) | 0–1 |
metallic |
Whether the surface is metallic | 0–1 |
const material = runtime.resources.loadMaterial("StandardMaterial.material");
const instance = runtime.resources.loadMaterialInstance(material);
instance.setProperty("albedo", vec4.fromValues(0.95, 0.64, 0.54, 1)); // copper colour
instance.setProperty("roughness", 0.1);
instance.setProperty("metallic", 1.0);
sphere.meshRenderComponent.material = instance;
// Release when done
instance.release();
Each object needs its own material instance (a copy of the material with its own property values), while the underlying material asset is shared. See Manual — Materials for the full API.
Without light, everything appears dark or uniformly lit. Cogs has a default headlight (a directional light attached to the camera), so you'll see something even with no explicit lights.
There are three types of light:
| Type | Description |
|---|---|
| Directional | Like the sun — parallel rays from a direction, no position, infinite range |
| Point | Like a light bulb — radiates in all directions from a position, limited range |
| Spot | Like a torch — a cone of light from a position in a direction |
Colours and intensities are physical values: intensity is in candela (point) or lux (directional). Start with values around 1–200 and adjust.
For the full lighting API including intensity units, headlight control, and how lighting interacts with materials, see Manual — Lighting and materials.
The camera is what the viewer looks through. It already exists in every scene — you access it via runtime.scene.camera.
The most important camera properties:
transformComponent)near or farther than far are not renderedIn practice you rarely set camera properties directly. Instead you use a camera controller such as OrbitingCameraController, which handles mouse/touch input for you automatically.
For camera controller setup, viewAll(), picking, and input event filters, see Manual — Interaction and navigation.
Before you can create any entities, you need to initialize Cogs. The essential pattern is:
import * as Cogs from "@kognifai/cogsengine";
// 1. Create a Control bound to a <canvas> element
const control = await Cogs.Control.create({
parent: canvasElement,
fetchHandler: fetchHandler,
onInitialized: () => initScene(),
});
// 2. Access the runtime (scene management, entity factories, resources)
const runtime = control.runtime;
// 3. Your scene setup goes here
const cube = runtime.Cube.create();
// 4. When unmounting, dispose everything
control.release();
The fetchHandler is a simple function that the engine uses to load its own internal assets. See Manual — Initializing for the full fetchHandler API, or the Angular based example below for ready-to-use integration boilerplate.
import { vec3, quat } from "gl-matrix";
const cube = runtime.Cube.create("MyCube");
cube.transformComponent.position = vec3.fromValues(3, 1, 0);
cube.transformComponent.scale = vec3.fromValues(2, 2, 2);
const rotation = quat.create();
quat.fromEuler(rotation, 0, 45, 0); // pitch, yaw, roll in degrees
cube.transformComponent.rotation = rotation;
runtime.scene.viewAll();
For all field types including matrices, quaternions, and array fields, see Manual — Working with fields.
Flat colour:
cube.materialComponent.diffuseColor = vec4.fromValues(0.2, 0.6, 1.0, 1.0); // blue
Image texture:
const texture = runtime.resources.loadTextureViaCogs("https://example.com/wood.jpg");
cube.materialComponent.diffuseMap = texture;
texture.release(); // release your handle; the texture stays alive while used by the entity
PBR material:
const material = runtime.resources.loadMaterial("StandardMaterial.material");
const instance = runtime.resources.loadMaterialInstance(material);
instance.setProperty("albedo", vec4.fromValues(1, 0.71, 0.29, 1)); // gold
instance.setProperty("roughness", 0.1);
instance.setProperty("metallic", 1.0);
sphere.meshRenderComponent.material = instance;
instance.release();
See Manual — Materials for the full API including transparency, blending, and sharing instances across entities.
GLB is the binary form of glTF — the most widely supported open 3D format. You can export GLB from Blender, 3ds Max, Maya, and many other tools.
const model = runtime.ModelEntity.create("MyModel");
model.modelComponent.model = runtime.resources.loadModel("https://example.com/mymodel.glb");
model.transformComponent.position = vec3.fromValues(0, 0, 0);
runtime.scene.viewAll();
// Cleanup
runtime.destroy(model);
Loading is asynchronous — the entity appears immediately, geometry renders once the file has downloaded. You do not need to await anything.
See Manual — Resources for resource lifecycle, handle ownership, and async loading patterns.
const light = runtime.Light.create("MyLight");
light.lightComponent.lightType = Cogs.LightType.Point;
light.lightComponent.intensity = 100; // candela
light.lightComponent.range = 20;
light.lightComponent.lightColor = vec4.fromValues(1, 0.95, 0.8, 1); // warm white
light.transformComponent.position = vec3.fromValues(0, 5, 5);
Directional light (sun-like):
const sun = runtime.Light.create("Sun");
sun.lightComponent.lightType = Cogs.LightType.Directional;
sun.lightComponent.intensity = 1.0;
const dir = quat.create();
quat.fromEuler(dir, -45, 30, 0);
sun.transformComponent.rotation = dir;
See Manual — Lighting and materials for intensity units, light types, and the default headlight.
const camera = runtime.scene.camera;
let cameraController = camera.getComponent(runtime.OrbitingCameraController);
if (!cameraController) {
cameraController = runtime.OrbitingCameraController.create();
camera.components.add(cameraController);
}
cameraController.cameraTarget = dvec3.fromValues(0, 0, 0);
cameraController.distance = 20;
cameraController.horizontalAngle = 0;
cameraController.verticalAngle = Math.PI / 2;
cameraController.maxDistance = 10000;
runtime.scene.viewAll();
// Focus on a specific entity
const bb = runtime.scene.getBoundingBoxWorld(myEntity);
Cogs.CameraUtils.viewAllCamera(runtime.scene.camera, bb, [0, 0]);
// Cleanup
camera.components.remove(cameraController);
runtime.destroy(cameraController);
See Manual — Interaction and navigation for picking, input event filters, and camera properties.
const parent = runtime.Cube.create("Parent");
const child = runtime.Sphere.create("Child");
child.transformComponent.position = vec3.fromValues(0, 0, 2); // 2 units in front of parent
parent.sceneComponent.children.add(child);
// Moving the parent moves the child too
parent.transformComponent.position = vec3.fromValues(5, 0, 0);
// child is now at world position [5, 0, 2]
parent.sceneComponent.visible = false; // hides the whole group
See Manual — Entities and Components for entity lifetime and ownership rules.
Cogs holds GPU resourcesthat are not garbage-collected automatically. Always release resources and destroy entities when a view is unmounted.
runtime.destroy(entity); // removes from scene
texture.release(); // release resource handle
materialInstance.release();
control.release(); // shut down the engine when unmounting
See Manual — Resource leaks for leak detection patterns and ownership rules.
You need Node.js (LTS recommended).
cd examples
npm install
npm run start
This launches live-server on port 9876. Open http://localhost:9876 in your browser to see the example gallery.
Each example entry has a title. To find its source, open examples/index.html, locate the title, and note the entry string (e.g. entry: "basicStandardMaterialExample"). Search for that function name in examples/js/Examples/ — that file is the example's source.
| Range | Topic |
|---|---|
| 0001–0010 | Initialization, entities, transforms, visibility, parenting, LOD |
| 0015–0028 | Meshes, blending, custom points, draw order |
| 0040–0041 | Lines |
| 0050–0053 | Input, picking, multi-draw |
| 0060–0074 | Cameras, world-to-screen, shadows, ambient occlusion |
| 0098–0108 | Textures (KTX2), billboards, axis cube, extrusion |
| 0110–0121 | Ocean, terrain, sky, video, marker sets, badge sets |
| 0131–0132 | Procedural sky, Potree point clouds |
| 0200–0210 | Model loading (GLB, glTF, Cogs model, asset hierarchies) |
| 0280–0299 | Height maps, tile layers, overlays, tracks |
| 0310–0360 | Oil & gas (casing, drilling, well logs, radial logs, 360° images) |
| 0500–0505 | Reservoir, physics, curtain view, multiphase flow, sea currents, vector field |
| 1000–1108 | Custom components, OGC 3D Tiles, twin visuals, highlight regions |
| 1338–1351 | Debug/visualisation (normals, draw calls, depth, depth-of-field, OIT) |
A few good example files to start with:
0002_EntityExample.js — creating and destroying entities0003_TransformExample.js — position, rotation, scale0008_LightExample.js — point lights0024_BasicStandardMaterialExample.js — PBR materials0063_OrbitingCameraControllerExample.js — camera navigation0205_GLBExample.js — loading GLB modelsWhen playing with examples you may want to add or symlink large custom datasets in the examples folder.
In that case you can try live-server's --watch=js to only watch source code changes.
A better alternative may be to serve them on a different port, but then adding live-server's --cors is usually needed.
Here we'll translate into practice all the high-level steps outlined in the introduction, using Cogs.js in the form of a TypeScript module.
You don't need to learn Angular just to follow this, but of course it is worth investing in learning the framework that you'll use to build your application.
This guide shows creating an Angular app displaying a Cube from scratch with 3D navigation to rotate and zoom the camera around the cube.
Cogs.js provides TypeScript Type Declarations for ease of use when developing TypeScript code.
import * as Cogs from '@kognifai/cogsengine';
import { dvec3 } from '@kognifai/cogsengine';
import { vec3, quat } from 'gl-matrix';
dvec3 is a double precision version of gl-matrix vec3. Imported specially to make it more like gl-matrix objects.
Some Common classes are:
// The main Cogs.js GUI control containing graphics.
class Cogs.Control;
// Runtime class where Entities are created (= control.runtime).
class Cogs.Runtime;
// Functionality for the 3D scene (= runtime.scene or control.scene).
class Cogs.Scene;
// Base class for all Cogs Entities. Camera/Cube/...
class Cogs.Entity;
// Base class for all Cogs Components.
class Cogs.Component;
// Class for resource handling Textures, Models, .
class Cogs.Resources;
This file can act as a quick guide to Cogs.js. Note that some of the definitions are a bit unusual for TypeScript code, but required in that context.
The type definitions can be found in the Path: node_modules/@kognifai/cogsengine/dist/cogs.esm.d.ts in your application.
npm install -g @angular/cli (Don't need Angular routing, Select CSS style)ng new --skip-git first-app (where you want it)cd first-appng serve --openctrl-c in the console to exit the development serverThis setup shows typical steps for how to add Cogs to an Angular application. Access right file, Packages required to run Cogs, Angular.json config to load Cogs and a very simple Cogs window showing a cube.
Steps to add Cogs and Angular to your project:
Required to allow installing KDI packages. See Adding Cogs to your project in Cogs.js Readme.md. Can use an existing copy from Cogs or KDI DigitalTwin.
npm install @kognifai/cogsengine
npm install gl-matrix
Required to make Cogs.js resource files and the WebAssembly binary available to Cogs.
Find the "assets" section in angular.json and replace it with:
"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "node_modules/@kognifai/cogsengine/dist/",
"output": "node_modules/@kognifai/cogsengine/dist/"
}
],
Put the following in src/app/app.component.css for fixes size canvas. Apply more elaborate layouts for own applications.
canvas {
width: 700px;
height: 600px;
margin: 20px;
}
Replace src/app/app.component.html with:
<header style="text-align:center">
<h3>{{ title }}</h3>
</header>
<div #myparent class="cogsparent"></div>
Replace src/app/app.component.ts with the following example code:
import { Component, ElementRef, DoCheck, ViewChild, OnDestroy, AfterViewInit } from '@angular/core';
import * as Cogs from '@kognifai/cogsengine';
import { dvec3 } from '@kognifai/cogsengine';
import { vec3 } from 'gl-matrix';
@Component({
selector: 'app-root',
standalone: true,
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements AfterViewInit, DoCheck, OnDestroy {
public title = 'Hello world Cogs!';
private _cogsControl: Cogs.Control | undefined;
private cogsReady = false;
private width = 0;
private height = 0;
// Used by Cogs FetchHandler
private cancellableFetches = new Map<number, XMLHttpRequest>();
/**
* View child of app component. Link to HTML def in 'app.component.html'
*/
@ViewChild('myparent')
private myparent: ElementRef | undefined;
/** Gets parent HTML element. Avoids testing undefined. */
private get parent(): ElementRef {
return this.myparent!;
}
/** Gets the created Cogs Control class. Avoids testing undefined. */
private get cogsControl() {
return this._cogsControl!;
}
initScene(): void {
console.log('APP initScene');
this.initializeNavigation();
this.cogsControl.runtime.Cube.create();
this.cogsReady = true;
}
/**
* Cogs does by default not provide any 3D navigation.
* A simple 3D navigation is included as a Cogs component.
* More complex application may provide its own navigation.
* @returns navigation component.
*/
initializeNavigation(): Cogs.OrbitingCameraController {
const runtime = this.cogsControl.runtime;
const camera = runtime.scene.camera;
// Ensure no existing controller component found.
// Can normally just create and add controller as next step below.
let cameraController = camera.getComponent(runtime.OrbitingCameraController);
if (!cameraController) {
cameraController = runtime.OrbitingCameraController.create();
camera.components.add(cameraController);
}
cameraController.cameraTarget = dvec3.fromValues(0, 0, 0);
cameraController.distance = 20;
cameraController.horizontalAngle = 0;
cameraController.verticalAngle = Math.PI / 2;
cameraController.maxDistance = 10000;
// Scene origin should be set near the centre of the scene.
// Important for large offsets like UTM coordinates.
runtime.scene.origin = dvec3.fromValues(0, 0, 0);
return cameraController;
}
/**
* Called first to initialize Cogs and set to the scene.
*/
async ngAfterViewInit(): Promise<void> {
const dataFetchHandler = this.createFetchHandler(this, true);
this._cogsControl = await Cogs.Control.create({
extensions: [],
variant: 'sdk',
parent: this.parent.nativeElement,
fetchHandler: dataFetchHandler,
onInitialized: () => this.initScene(),
print: function (text) {
console.log('print', text);
},
});
this.cogsControl.domElement.oncontextmenu = function (e: any) {
e.preventDefault();
};
}
// Optional cleanup.
ngOnDestroy(): void {
this.cogsControl.runtime.clear();
this.cogsControl.domElement.remove();
}
ngDoCheck(): void {
if (
this.cogsReady &&
(this.width !== this.parent.nativeElement.offsetWidth || this.height !== this.parent.nativeElement.offsetHeight)
) {
this.width = this.parent.nativeElement.offsetWidth;
this.height = this.parent.nativeElement.offsetHeight;
console.log('width, height', this.width, this.height);
this.sizeChanged(this.width, this.height);
}
}
sizeChanged(width: number, height: number): void {
this.cogsControl.domElement.style.width = width + '';
this.cogsControl.domElement.style.height = height + '';
this.cogsControl.runtime.resize(width, height);
}
/**
* Creates FetchHandler with request cancellation for Cogs requests
* @param owner - Angular Application
* @param logging - TRUE if logging the data requests.
* @returns Returns FetchHandler to be passed to Cogs Control.create
*/
private createFetchHandler(owner: AppComponent, logging: boolean): Cogs.IFetchHandler {
const fetcher: Cogs.IFetchHandler = {
fetch: function (url: string, offset: number, size: number, handler: Cogs.FetchResponseHandler, fetchId: number): boolean {
if (logging) {
cogsPrint(`[Debug] Test Application - fetchHandler.fetcher(${url}, ${fetchId} (${offset}, ${size}))`);
}
const xhr = new XMLHttpRequest();
if (typeof fetchId === 'number') {
owner.cancellableFetches.set(fetchId, xhr);
}
xhr.responseType = 'arraybuffer';
xhr.onloadend = function () {
owner.cancellableFetches.delete(fetchId);
handler(xhr.status === 200 || xhr.status === 206 ? xhr.response : undefined, xhr.status);
};
xhr.open('GET', url);
if (0 < size) {
const len = offset + Math.max(1, size) - 1;
const range = `bytes=${offset}-${len}`;
xhr.setRequestHeader('Range', range);
}
xhr.send();
return true;
},
cancel: function (fetchId: number): void {
if (logging) {
cogsPrint(`[Info] Test Application - fetchHandler.cancel(${fetchId})`);
}
const xhr = owner.cancellableFetches.get(fetchId);
if (xhr) {
xhr.abort();
}
},
};
return fetcher;
}
}
// Cogs Logging Levels= Trace=0, Debug=1, Info=2, Warning=3, Error=4,Fatal=5
const Category = {
Trace: 0,
Debug: 1,
Info: 2,
Warning: 3,
Error: 4,
Fatal: 5,
};
/** Logging level for Tests: 0= Trace+All */
let logLevel = Category.Trace;
/**
* Cogs Logging. Cogs Log entries are of type: [Trace/Debug/Info/Warning/Error/Fatal][module] text
* Default is skip Debug/Info me
*/
export function cogsPrint(text: string): void {
if (text.startsWith('[Trace]')) {
if (logLevel <= Category.Trace) {
console.trace(text);
}
} else if (text.startsWith('[Debug]')) {
if (logLevel <= Category.Debug) {
console.debug(text);
}
} else if (text.startsWith('[Info')) {
if (logLevel <= Category.Info) {
console.info(text);
}
} else if (text.startsWith('[Warn')) {
if (logLevel <= Category.Warning) {
console.warn(text);
}
} else if (text.startsWith('[Error') || text.startsWith('[Fatal')) {
console.error(text);
} else {
console.log('[Unknown] ' + text);
}
}
The cogsPrint and createFetchHandler functions can typically be reused as a first step.
Run npm start (defined as "ng serve" in package.json)
Open link to web-page in browser shown when starting the angular server.
| Document | When to read it |
|---|---|
| Manual | Coordinate systems, picking, precision, resource lifecycle, WebXR |