Skip to content

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:

bash
npm install small-world

Basic 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:

typescript
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.

tsx
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!

Released under the MIT License.