Skip to content

Architecture & Code Showcases

The Small World Engine has a modular design and uses composition over deep inheritance. Below is an overview of the most important classes, interfaces, and parameters that you will work with on a daily basis, as well as concrete code examples to get you started.

API Reference

For a complete list of all classes, methods, and type declarations (incl. constructor parameters), please open the automatically generated API Reference.


1. Scene Graph (Object3D)

The heart of the engine is the Object3D class. Everything that exists in the world (meshes, cameras, virtual anchors) is or inherits from Object3D. It manages the local and global transformation matrices, geometry, and material.

Showcase: Creating and placing an object

typescript
import { Object3D, Cube, StandardMaterial, Color } from "small-world";

const player = new Object3D("Player");

// Position (X=Right, Y=Up, Z=Backward)
player.position.set(0, 1, 0);

// Scale and Rotation
player.scale.set(2, 2, 2);
player.rotation.y = Math.PI / 4; // 45 degrees

// Assign geometry and material
player.geometry = new Cube({ size: 1 }).getGeometryData();
player.material = new StandardMaterial({
  color: Color.RED,
  metallic: 0.1,
  roughness: 0.8,
});

// Attach child objects (Hierarchy)
const weapon = new Object3D("Weapon");
weapon.position.set(1, 0, 0); // Relative to the player!
player.add(weapon);

// Add to the scene
this.scene.add(player);

2. Cameras & Controllers

The engine does not use a rigid camera model. Instead, there is a base Camera that is controlled by flexible strategies (like SmoothStrategy, IsometricStrategy) and controllers (FPSController, ZoomController).

Showcase: First-Person Shooter Camera (FPS)

typescript
import { FPSController, CameraStrategyType } from "small-world";

// The strategy defines how the camera updates (STIFF = direct, SMOOTH = interpolated)
this.camera.setStrategy(CameraStrategyType.STIFF);

// Controllers are Behaviors – attach them directly to the camera.
// Do NOT use this.controllers.push() (deprecated pattern).
this.camera.addBehavior(
  new FPSController({
    moveSpeed: 10.0,
    lookSensitivity: 0.002,
  })
);

3. Materials (PBR & Specific Shaders)

Small World uses a hybrid rendering approach (WebGL2 & WebGPU) based on the Cook-Torrance BRDF model. Materials define parameters that are read by the shader.

Overview of key materials

  • StandardMaterial: For 90% of objects. Supports color, metallic, roughness, as well as diffuse, normal, and roughness maps.
  • GlassMaterial: A refractive material for glass or water with true refraction (ior), configurable thickness, and transmission.
  • SpriteMaterial: For 2D/2.5D billboards that always face the camera.

Showcase: Glass/Water Material with Index of Refraction

typescript
import { GlassMaterial, Color } from "small-world";

const water = new GlassMaterial({
  color: new Color(0.9, 0.95, 1.0),
  roughness: 0.05,
  ior: 1.33, // Index of Refraction for water
  thickness: 2.0, // Affects refraction depth
  transmission: 0.9, // How much light passes through vs. reflects
});

waterSurface.material = water;

4. Behaviors & Finite State Machines (FSM)

You shouldn't write complex logic into a giant update() loop. Instead, use the Behavior system to attach isolated logic blocks (components) to an Object3D.

Showcase: A Pulse Behavior

typescript
import { Behavior, Object3D } from "small-world";

export class PulseBehavior extends Behavior {
  private _speed: number;
  private _baseScale: number;
  private _elapsed: number = 0;

  constructor(speed: number = 2.0) {
    super();
    this._speed = speed;
    this._baseScale = 1.0;
  }

  // Called when the behavior is attached to the object via obj.addBehavior()
  public override onAttach(target: Object3D): void {
    this._baseScale = target.scale.x;
  }

  // Called automatically every frame by the Scene, only receives deltaTime
  public override update(deltaTime: number): void {
    if (!this.target) return;
    this._elapsed += deltaTime;

    // Calculate sine pulse
    const scale = this._baseScale + Math.sin(this._elapsed * this._speed) * 0.2;
    this.target.scale.set(scale, scale, scale);
  }
}

// Usage:
const heart = new Object3D("Heart");
heart.addBehavior(new PulseBehavior(5.0));

If states become more complex (e.g., IDLE -> WALK -> ATTACK), use the built-in StateMachine module, which integrates seamlessly with Behaviors via StateMachineBehavior.


5. Resource Management & Garbage Collection

Unlike older graphics engines where you must manually call dispose() on geometries, textures, and materials to prevent GPU memory leaks, Small World uses automated internal Reference Counting.

How it works

Every geometry buffer, shader program, and texture is tracked by the active renderer (WebGL1, WebGL2, or WebGPU). When you remove an Object3D from the Scene, the engine decrements the reference counts for the object's resources. If a resource's reference count drops to zero, the engine automatically queues it for deletion and safely destroys the underlying GPU object.

typescript
// Adding an object increments reference counts for its geometry and material textures
this.scene.add(myObject);

// ... Later ...

// Removing the object decrements the reference counts.
// If no other objects use the same geometry/textures, they are automatically purged from VRAM!
this.scene.remove(myObject);

(Note: RenderTarget textures are excluded from this automated cleanup, as their lifecycles are explicitly managed by the render pipeline rather than individual objects.)

Released under the MIT License.