JavaScript — a lit scene
The 3d profile adds the scene graph: meshes, cameras and lights, with the same
retained model the C API uses.
The code
Section titled “The code”import { createModkit } from '@modkit/core';
const runtime = await createModkit({ canvas, profile: '3d' });const scene = runtime.createScene('demo');
const camera = scene.createPerspectiveCamera({ position: [0, 1.6, 4.5], target: [0, 0, 0], fov: 50,});
scene.createDirectionalLight({ direction: [-0.4, -1, -0.6], color: [1, 0.97, 0.92], intensity: 1.1,});
const box = scene.createMesh({ geometry: runtime.geometry.box([1, 1, 1]), material: runtime.material.lit({ color: '#5f6fd8', shininess: 32 }),});
const sphere = scene.createMesh({ geometry: runtime.geometry.sphere(0.55, 32), material: runtime.material.lit({ color: '#d9d9dd', shininess: 64 }),});
runtime.onFrame(() => { const t = runtime.time; box.setTransform({ rotation: [t * 0.4, t * 0.7, 0] }); sphere.setTransform({ position: [Math.cos(t) * 1.9, 0.2, Math.sin(t) * 1.9] }); scene.render({ camera, clearColor: '#111114' });});
runtime.start();A few things worth noting, because they are easy to get wrong from the type signatures alone:
createMeshreturns aSceneMesh, and aSceneMeshis aSceneNode. There is no.nodeto reach through — callsetTransformon the mesh itself.- Material and clear colours are
Color, which is a hex string or a packed number. Light colours areVec3. They are not the same type. scene.render({ camera })both clears and draws. You do not open a pass yourself unless you want the explicit path shown in hello canvas.
Using it from React
Section titled “Using it from React”The demo above is a React island. The pattern is an effect that creates the runtime and disposes it on unmount:
useEffect(() => { let disposed = false; let runtime: ModkitRuntime | null = null;
createModkit({ canvas: canvasRef.current, profile: '3d' }).then((rt) => { // The effect can be torn down while wasm is still instantiating. if (disposed) return rt.dispose(); runtime = rt; rotatingScene(rt); rt.start(); });
return () => { disposed = true; runtime?.dispose(); };}, []);The disposed flag is not paranoia. Wasm instantiation takes long enough that a fast
navigation will unmount the component before the promise settles, and without the
guard you leak a runtime that nothing will ever stop.