Porting from Babylon.js to Babylon Lite

This guide shows how to translate a Babylon.js (BJS) scene to Babylon Lite, side by side. Babylon Lite uses factory functions instead of constructors, plain data instead of class instances, and explicit addToScene() instead of auto-registration.


Quick Reference

Babylon.jsBabylon Lite
Babylon.js
new WebGPUEngine(canvas); await engine.initAsync()
Babylon Lite
const engine = await createEngine(canvas)
Babylon.js
new Scene(engine)
Babylon Lite
createSceneContext(engine)
Babylon.js
engine.runRenderLoop(() => scene.render())
Babylon Lite
await startEngine(engine)
Babylon.js
new ArcRotateCamera("cam", α, β, r, target, scene)
Babylon Lite
createArcRotateCamera(α, β, r, target)
Babylon.js
new FreeCamera("cam", position, scene)
Babylon Lite
createFreeCamera(position, target)
Babylon.js
scene.createDefaultCamera(true, true, true)
Babylon Lite
createDefaultCamera(scene)
Babylon.js
camera.attachControl(canvas, true)
Babylon Lite
attachControl(camera, canvas, scene) (arc-rotate) / attachFreeControl(camera, canvas, scene) (free)
Babylon.js
new HemisphericLight("h", new Vector3(0,1,0), scene)
Babylon Lite
createHemisphericLight([0,1,0], 1.0)
Babylon.js
new DirectionalLight("d", new Vector3(0,-1,0), scene)
Babylon Lite
createDirectionalLight([0,-1,0])
Babylon.js
new SpotLight("s", pos, dir, angle, exp, scene)
Babylon Lite
createSpotLight(pos, dir, angle, exp)
Babylon.js
MeshBuilder.CreateSphere("s", {}, scene)
Babylon Lite
createSphere(engine)
Babylon.js
MeshBuilder.CreateBox("b", {}, scene)
Babylon Lite
createBox(engine)
Babylon.js
MeshBuilder.CreateGround("g", {}, scene)
Babylon Lite
createGround(engine, opts)
Babylon.js
new StandardMaterial("mat", scene)
Babylon Lite
createStandardMaterial()
Babylon.js
new PBRMaterial("pbr", scene)
Babylon Lite
createPbrMaterial()
Babylon.js
new GridMaterial("grid", scene) (@babylonjs/materials)
Babylon Lite
createGridMaterial(opts)
Babylon.js
SceneLoader.ImportMeshAsync("", url, file, scene)
Babylon Lite
addToScene(scene, await loadGltf(engine, url))
Babylon.js
new CubeTexture(url, scene) + createDefaultEnvironment()
Babylon Lite
await loadEnvironment(scene, url, opts)
Babylon.js
new Texture(url, scene)
Babylon Lite
await loadTexture2D(engine, url)
Babylon.js
KTX1 compressed 2D texture
Babylon Lite
await loadKtxTexture2D(engine, baseUrl, suffixes)
Babylon.js
glTF KTX2 / KHR_texture_basisu texture source
Babylon Lite
addToScene(scene, await loadGltf(engine, ktx2GltfUrl)) (auto-detected)
Babylon.js
Basis Universal (.basis) 2D texture
Babylon Lite
await loadBasisTexture2D(engine, url)
Babylon.js
new ShadowGenerator(size, light) with a directional light and ESM
Babylon Lite
createEsmDirectionalShadowGenerator(engine, light, opts)
Babylon.js
sg.usePercentageCloserFiltering = true with a spotlight
Babylon Lite
createPcfSpotlightShadowGenerator(engine, light, opts)
Babylon.js
sg.usePercentageCloserFiltering = true with a directional light
Babylon Lite
createPcfDirectionalShadowGenerator(engine, light, opts)
Babylon.js
mesh.thinInstanceSetBuffer("matrix", data, 16)
Babylon Lite
setThinInstances(mesh, data, count)
Babylon.js
mesh.thinInstanceSetBuffer("color", data, 4)
Babylon Lite
setThinInstanceColors(mesh, data)
Babylon.js
new Vector3(x, y, z)
Babylon Lite
{ x, y, z } or [x, y, z]
Babylon.js
new Color3(r, g, b)
Babylon Lite
[r, g, b]
Babylon.js
Matrix.Identity()
Babylon Lite
mat4Identity()
Babylon.js
mesh.dispose()
Babylon Lite
removeFromScene(scene, mesh)
Babylon.js
scene.onBeforeRenderObservable.add(fn)
Babylon Lite
onBeforeRender(scene, fn)

Key Differences

1. No Scene in Constructors

BJS objects take scene in their constructor and auto-register. Lite objects are plain data — you create them, then addToScene() them explicitly.

// ❌ Babylon.js
const light = new HemisphericLight("light", new Vector3(0, 1, 0), scene);
// ✅ Babylon Lite
const light = createHemisphericLight([0, 1, 0], 1.0);
addToScene(scene, light);

2. Engine & Render Loop

BJS uses runRenderLoop with a callback. Lite uses a single startEngine(engine) that returns a promise resolving after the first frame.

// ❌ Babylon.js
const engine = new WebGPUEngine(canvas);
await engine.initAsync();
const scene = new Scene(engine);
// ... setup ...
engine.runRenderLoop(() => scene.render());
// ✅ Babylon Lite
const engine = await createEngine(canvas);
const scene = createSceneContext(engine);
// ... setup ...
await startEngine(engine);

3. Plain Data, Not Classes

Lite uses plain objects, arrays, and Float32Array instead of BJS classes like Vector3, Color3, Matrix.

// ❌ Babylon.js
light.direction = new Vector3(0, -1, 0);
light.diffuse = new Color3(1, 0, 0);
// ✅ Babylon Lite
const light = createDirectionalLight([0, -1, 0]);
light.diffuse = [1, 0, 0];

4. Camera Controls Are Separate

BJS cameras have attachControl as a method. Lite separates camera data from input handling.

// ❌ Babylon.js
const camera = new ArcRotateCamera("cam", -Math.PI/2, Math.PI/2, 5, Vector3.Zero(), scene);
camera.attachControl(canvas, true);
// ✅ Babylon Lite
const camera = createArcRotateCamera(-Math.PI/2, Math.PI/2, 5, { x: 0, y: 0, z: 0 });
scene.camera = camera;
attachControl(camera, canvas, scene);

5. Loaders and Scene Registration

loadEnvironment() adds its environment data/renderables to the scene internally. loadGltf() returns an asset container; pass it to addToScene() so transform-node hierarchies, meshes, and animation groups are registered explicitly.

// ❌ Babylon.js
await SceneLoader.ImportMeshAsync("", baseUrl, "model.glb", scene);
scene.environmentTexture = new CubeTexture(envUrl, scene);
scene.createDefaultEnvironment({ createSkybox: true, skyboxSize: 1000 });
// ✅ Babylon Lite
addToScene(scene, await loadGltf(engine, "model.glb"));
await loadEnvironment(scene, envUrl, {
skyboxUrl: "skybox.dds",
skyboxSize: 1000,
groundTextureUrl: "ground.png",
brdfUrl: "/brdf-lut.png",
});

6. Shadows Attach to Lights

BJS creates a ShadowGenerator separately. Lite assigns it directly to the light.

// ❌ Babylon.js
const sg = new ShadowGenerator(1024, light);
sg.addShadowCaster(mesh);
sg.useBlurExponentialShadowMap = true;
ground.receiveShadows = true;
// ✅ Babylon Lite
light.shadowGenerator = createEsmDirectionalShadowGenerator(engine, light, {
mapSize: 1024,
depthScale: 50,
blurScale: 2,
});
setShadowTaskCasterMeshes(light.shadowGenerator, [mesh]);
ground.receiveShadows = true;
await registerSceneWithShadowSupport(scene);

For PCF shadows:

// ❌ Babylon.js
const sg = new ShadowGenerator(1024, spotLight);
sg.usePercentageCloserFiltering = true;
// ✅ Babylon Lite
spotLight.shadowGenerator = createPcfSpotlightShadowGenerator(engine, spotLight, {
mapSize: 1024,
});
setShadowTaskCasterMeshes(spotLight.shadowGenerator, [mesh]);
await registerSceneWithShadowSupport(scene);

7. Thin Instances Use Raw Arrays

No Matrix class needed. Pass raw Float32Array with 16 floats per instance.

// ❌ Babylon.js
const matrices = new Float32Array(count * 16);
// ... fill with Matrix values ...
mesh.thinInstanceSetBuffer("matrix", matrices, 16);
mesh.thinInstanceSetBuffer("color", colors, 4);
// ✅ Babylon Lite
const matrices = new Float32Array(count * 16);
// ... fill directly (column-major 4x4) ...
setThinInstances(mesh, matrices, count);
setThinInstanceColors(mesh, colors);
addToScene(scene, mesh);

8. Mesh Factories Take Engine, Not Scene

BJS mesh builders take scene. Lite mesh factories take engine (for GPU buffer creation) and return plain mesh data.

// ❌ Babylon.js
const sphere = MeshBuilder.CreateSphere("sphere", { diameter: 2 }, scene);
sphere.material = new StandardMaterial("mat", scene);
// ✅ Babylon Lite
const sphere = createSphere(engine);
sphere.material = createStandardMaterial();
addToScene(scene, sphere);

9. Removing & Disposing Entities

BJS uses mesh.dispose() on individual objects. Lite uses removeFromScene() which removes the mesh from the scene and destroys all its GPU resources (buffers, textures, skeleton data).

// ❌ Babylon.js
sphere.dispose();
// ✅ Babylon Lite
removeFromScene(scene, sphere);

For full teardown:

// ✅ Babylon Lite — tear down everything
disposeScene(scene); // releases all meshes, renderables, disposables
disposeEngine(engine); // destroys GPU device, render targets, swapchain

Full Example: Porting a PBR Scene

Babylon.js

const engine = new WebGPUEngine(canvas);
await engine.initAsync();
const scene = new Scene(engine);
scene.clearColor = new Color4(0.2, 0.2, 0.3, 1);
const light = new HemisphericLight("h", new Vector3(0, 1, 0), scene);
light.intensity = 1.0;
await SceneLoader.ImportMeshAsync("", baseUrl, "BoomBox.glb", scene);
const envTex = new CubeTexture(envUrl, scene);
scene.environmentTexture = envTex;
scene.createDefaultCamera(true, true, true);
scene.createDefaultEnvironment({ skyboxSize: 1000 });
engine.runRenderLoop(() => scene.render());

Babylon Lite

const engine = await createEngine(canvas);
const scene = createSceneContext(engine);
addToScene(scene, await loadGltf(engine, "BoomBox.glb"));
await loadEnvironment(scene, envUrl, {
skyboxUrl: "skybox.dds",
skyboxSize: 1000,
groundTextureUrl: "ground.png",
brdfUrl: "/brdf-lut.png",
});
const cam = createDefaultCamera(scene);
attachControl(cam, canvas, scene);
addToScene(scene, createHemisphericLight([0, 1, 0], 1.0));
await startEngine(engine);

Gotchas

GotchaDetails
Gotcha
No auto-add
Details
Meshes, lights, transform nodes, and loadGltf() asset containers must be explicitly added with addToScene(). loadEnvironment() adds its environment data/renderables internally.
Gotcha
No new keyword
Details
Everything is created via factory functions, not constructors.
Gotcha
Assign camera explicitly
Details
Either use createDefaultCamera(scene) (auto-assigns) or set scene.camera = myCamera manually.
Gotcha
Materials are optional
Details
createStandardMaterial() / createPbrMaterial() return props objects. Assign to mesh.material.
Gotcha
WebGPU only
Details
No WebGL fallback. createEngine() throws if WebGPU is unavailable.
Gotcha
No dispose() on meshes
Details
Use removeFromScene(scene, mesh) to remove a single mesh and destroy its GPU resources. Use disposeScene(scene) + disposeEngine(engine) to tear down everything.
Gotcha
Tree-shakable imports
Details
Import only what you use. Unused features are stripped from the bundle.
Gotcha
KTX2 is glTF-scoped
Details
KTX1 has a direct loadKtxTexture2D() helper. KTX2/BasisU texture sources are handled through glTF KHR_texture_basisu during loadGltf() so non-KTX2 scenes pay zero runtime bundle cost.
Gotcha
Material property animation
Details
Mutating material props at runtime requires marking the material dirty. See Material Animation section below.

Material Animation

Babylon Lite supports animating material properties at runtime (e.g. changing colors, alpha, anisotropy intensity per frame). Two approaches are available:

Manual (default — zero overhead)

Mutate the property, then call markMaterialUboDirty():

import { markMaterialUboDirty } from "@babylonjs/lite";
onBeforeRender(scene, () => {
material.alpha = Math.sin(time) * 0.5 + 0.5;
markMaterialUboDirty(material);
});

This works for both PBR and Standard materials. Zero runtime cost when nothing changes.

Automatic tracking (opt-in)

Call enableMaterialTracking() once on a material to install property setters that auto-detect changes — including in-place array mutations like material.diffuseColor[0] = 0.5:

import { enableMaterialTracking } from "@babylonjs/lite";
const mat = createPbrMaterial({ anisotropy: { isEnabled: true, intensity: 1.0 } });
enableMaterialTracking(mat);
// Now mutations auto-mark the material UBO dirty — no manual call needed:
onBeforeRender(scene, () => {
mat.anisotropy!.intensity = Math.cos(a) * 0.5 + 0.5; // auto-dirty
mat.emissiveColor![0] = 0.5; // auto-dirty (index write)
});

enableMaterialTracking is fully tree-shakable — scenes that don't import it pay zero bundle cost.

FeaturemarkMaterialUboDirtyenableMaterialTracking
Feature
Bundle cost
markMaterialUboDirty
~50 bytes
enableMaterialTracking
~1.5 KB (only if imported)
Feature
Per-frame cost
markMaterialUboDirty
Zero (manual call)
enableMaterialTracking
Zero (setter fires only on change)
Feature
Catches color[0] = x
markMaterialUboDirty
❌ (must call manually)
enableMaterialTracking
Feature
Catches mat.alpha = x
markMaterialUboDirty
❌ (must call manually)
enableMaterialTracking

Material Stencil (opt-in)

Babylon Lite supports a per-material stencil test baked into the main color pass — for masking effects like portals and decals (one material writes the stencil buffer where it draws, another discards fragments where the stencil was written). It is an explicit opt-in so stencil-free scenes stay byte-near-identical:

import { createStandardMaterial, enableMaterialStencil, registerScene } from "@babylonjs/lite";
// Writer: stamp the stencil buffer (0 → 1) everywhere it draws.
const mask = createStandardMaterial();
mask.stencil = { passOp: "increment-clamp" };
// Tester: draw only where the stencil is still the pass's default reference of 0
// (i.e. where the writer did NOT draw). No dynamic stencil reference needed.
const masked = createStandardMaterial();
masked.stencil = { compare: "equal" };
enableMaterialStencil(); // ← opt-in, BEFORE registerScene
await registerScene(scene);

StencilState accepts compare, passOp, failOp, depthFailOp, readMask, and writeMask (all optional; defaults "always" / "keep" / 0xff). Stencil is applied only on a stencil-capable target (the main color pass) and ignored on depth-only/shadow passes. Without calling enableMaterialStencil, a material's stencil field is inert and the pipeline builders carry no stencil code — enableMaterialStencil is fully tree-shakable, so scenes that don't import it pay no bundle cost.


glTF / PBR Extensions

Babylon Lite's glTF loader + PBR material understand the following extensions. Each feature is tree-shakable: scenes that don't use it pay no bundle cost.

Extension / FeatureSupportNotes
Extension / Feature
KHR_materials_pbrSpecularGlossiness
Support
Notes
Auto-detected by loadGltf()
Extension / Feature
KHR_materials_clearcoat
Support
Notes
Auto-detected; or createPbrMaterial({ clearCoat: { ... } })
Extension / Feature
KHR_materials_sheen
Support
Notes
Auto-detected (BJS-spec albedo scaling for glTF); or createPbrMaterial({ sheen: { ... } })
Extension / Feature
KHR_materials_anisotropy
Support
Notes
Auto-detected; or createPbrMaterial({ anisotropy: { ... } })
Extension / Feature
KHR_materials_variants
Support
Notes
selectVariant(scene, name), getVariantNames(scene), resetVariant(scene)
Extension / Feature
KHR_materials_ior
Support
Notes
Auto-detected; index of refraction for dielectrics (Scene 30)
Extension / Feature
KHR_materials_specular
Support
Notes
Auto-detected; dielectric specular intensity + color (Scene 30)
Extension / Feature
KHR_materials_volume
Support
Notes
Auto-detected; attenuation color/distance + thickness (Scene 30)
Extension / Feature
KHR_materials_transmission
Support
Notes
Frame-graph scene-texture transmission for transmissive glTF materials (Scenes 30/33/112). Screen-space scene-texture refraction; parity is within-5 = 100% of pixels.
Extension / Feature
KHR_texture_transform
Support
Notes
Auto-resolved at load (material-wide UV transform)
Extension / Feature
KHR_texture_basisu
Support
Notes
Auto-detected; dynamically loads KTX2 decoder/upload path only for glTF assets that declare the extension (Scene 112)
Extension / Feature
EXT_texture_webp
Support
Notes
Auto-detected through texture source selection; image decode is browser-native (Scene 37)
Extension / Feature
KHR_draco_mesh_compression
Support
Notes
Auto-detected; loads draco_decoder.js + .wasm on demand from site root (override via setDracoBaseUrl())
Extension / Feature
KHR_materials_emissive_strength
Support
Notes
Auto-detected; multiplies emissive output (Scene 31)
Extension / Feature
KHR_materials_unlit
Support
Notes
Auto-detected; emits base color directly with no lighting (Scene 32)
Extension / Feature
KHR_lights_punctual
Support
Notes
Auto-detected; point / spot / directional lights baked from glTF nodes (Scene 33)
Extension / Feature
KHR_node_visibility
Support
Notes
Auto-detected; per-node visibility flag honoured at render time (Scene 34)
Extension / Feature
KHR_animation_pointer
Support
Notes
Auto-detected; animates arbitrary JSON pointers (e.g. node visibility, material UBO fields) (Scene 34)
Extension / Feature
EXT_mesh_gpu_instancing
Support
Notes
Auto-detected; per-node TRS accessors expanded into thin instances (Scene 35)
Extension / Feature
EXT_meshopt_compression
Support
Notes
Auto-detected; meshopt-decodes vertex/index buffers via a dynamically-imported decoder (Scene 211)
Extension / Feature
KHR_mesh_quantization
Support
Notes
Auto-detected; normalized/quantized vertex attributes uploaded with native typed formats (Scene 211)
Extension / Feature
KHR_xmp_json_ld
Support
Notes
Auto-detected; JSON-LD metadata packets surfaced on AssetContainer.xmpMetadata with zero render impact (Scene 210)
Extension / Feature
Interleaved vertex buffers
Support
Notes
Genuine GPU-level interleave: a strided bufferView is uploaded once and bound to each attribute slot via arrayStride/offset — no CPU de-interleave or asset rewrite (Scene 210)
Extension / Feature
Subsurface translucency + thickness
Support
Notes
createPbrMaterial({ subsurface: { translucency, thickness } })
Extension / Feature
Specular anti-aliasing
Support
Notes
Auto-on for glTF; manual: createPbrMaterial({ enableSpecularAA: true })
Extension / Feature
Morph targets
Support
Notes
PBR meshes only (not StandardMaterial)
Extension / Feature
Skeletal animation (4 or 8 bones)
Support
Notes
Driven by createAnimationController(scene)
Extension / Feature
Animation blending / weights / additive clips
Support
Notes
AnimationManager with setAnimationWeight(), crossFadeAnimationGroups(), and setAnimationAdditive() (Scenes 155-158)
Extension / Feature
ShaderMaterial
Support
Notes
WGSL-only createShaderMaterial() with typed uniforms, samplers, defines, alpha blend/test (Scenes 159-163)
Extension / Feature
GridMaterial
Support
Notes
Procedural unlit object-space grid via createGridMaterial(): mainColor/lineColor, gridRatio, gridOffset, major/minor units, opacity, antialias, useMaxLine, preMultiplyAlpha, opacityTexture, visibility (Scene 213)
Extension / Feature
Node Material
Support
Notes
NME snippet parser covering core, PBR, math, texture, procedural, normal, screen/depth, matrix, loop, and storage blocks (Scenes 60-89)
Extension / Feature
Sprites / billboards
Support
Notes
2D layers, depth-hosted sprites, facing/axis-locked/cutout billboards; not the full BJS SpriteManager API (Scenes 50-57)
Extension / Feature
Gaussian splatting
Support
Notes
.ply, .splat, .sog, .spz, bake transforms, material plugin fragments (Scenes 120-126)
Extension / Feature
CSG / CSG2
Support
Notes
Mesh boolean subtract/intersect/union/add APIs (Scenes 90-91)
Extension / Feature
Physics
Support
Notes
Havok Physics V2 subset (Scene 40)
Extension / Feature
Navigation / Recast
Support
Notes
Recast V2 navmesh, crowd pathing, tile-cache obstacles, off-mesh links, raycast (Scenes 170-175)
Extension / Feature
Device-lost recovery
Support
Notes
Opt-in WebGPU device-loss recovery (Scene 164)
Extension / Feature
Screen-space SSS (PrePass)
Support
Notes
Not implemented — only BRDF-layer translucency

See lab/lite/src/lite/scene*.ts for end-to-end examples of each extension in action.