Getting Started
Small World is a lightweight, high-performance, modular 3D game engine for the web built with TypeScript.
Installation
Install the package via NPM:
npm install small-worldBasic Setup
The engine uses a strategy-based lifecycle. You subclass SmallWorld (or AbstractShowcase, which adds a few demo/debug conveniences on top of SmallWorld) and override the setupScene and update lifecycle methods.
1. Basic Showcase Implementation
Create a file named app.ts to boot the engine:
import {
AbstractShowcase,
Color,
Cube,
Object3D,
RendererType,
StandardMaterial,
} from "small-world";
class MyFirstWorld extends AbstractShowcase {
protected override async setupScene(): Promise<void> {
// 1. Create a green PBR cube
const cubeObj = new Object3D("RotatingCube");
cubeObj.geometry = new Cube({ size: 1.5 }).getGeometryData();
cubeObj.material = new StandardMaterial({
color: Color.GREEN,
metallic: 0.5,
roughness: 0.3,
});
cubeObj.setPosition(0, 1.0, 0);
// 2. Add to scene
this.scene.add(cubeObj);
// 3. Move the camera back to view the scene
this.camera.position.set(0, 3.0, 6.0);
this.camera.target.set(0, 1.0, 0);
}
protected override update(deltaTime: number): void {
super.update(deltaTime);
// Rotate the cube object
const cube = this.scene.getObjectByName("RotatingCube");
if (cube) {
cube.rotation.y += 1.0 * deltaTime;
}
// Tick/render loop
this.scene.update(deltaTime);
}
}
// Instantiate and start
const app = new MyFirstWorld({
rendererType: RendererType.BEST,
});
app.start().then(() => {
console.log("Small World initialized!");
});2. SPA & Framework Integration (React / Vue / Angular)
When embedding Small World inside a Single Page Application (SPA), the browser does not automatically refresh when you change routes. To prevent memory leaks or multiple render loops running simultaneously in the background, you must cleanly destroy the engine when your component unmounts.
Simply call the destroy() method. This will instantly halt the requestAnimationFrame loop, detach all global window event listeners, and flush the WebGPU/WebGL memory.
import { useEffect, useRef } from "react";
import { MyFirstWorld } from "./MyFirstWorld";
export function GameComponent() {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
let app: MyFirstWorld | null = null;
if (canvasRef.current) {
app = new MyFirstWorld({
canvasId: "SmallWorldCanvas"
});
app.start();
}
return () => {
// Clean up the engine completely when the React component unmounts!
if (app) {
app.destroy();
}
};
}, []);
return <canvas id="SmallWorldCanvas" ref={canvasRef} />;
}Note: The engine features an automatic safety net out of the box. If it detects that its canvas element has been forcefully removed from the DOM by a framework without destroy() being explicitly called, it will catch this and auto-destroy itself safely!