3D Scene Creator API v1.0.1

API Reference

SceneCreator is a single class that wraps a Three.js scene, camera, renderer and orbit controls, with helpers for loading, lighting, animation, physics and picking. Every configuration method returns this, so calls chain.

$ pnpm add 3d-scene-creator three
import { SceneCreator } from "3d-scene-creator";
import * as THREE from "three";

const scene = new SceneCreator(container)
  .addLighting()
  .addControls()
  .addSkybox("/panorama.jpg");
three is a peer dependency (install it yourself). cannon-es (MIT) powers the physics API and is imported on demand, so bundlers code-split it: apps that never call enablePhysics() don't load it.

๐Ÿ—๏ธ Constructor

new SceneCreator(container?: HTMLElement, scale?: number, camPos?: Vector3, targetPos?: Vector3)

Create a scene. If a container is given, the renderer is attached and the render loop starts immediately.

container optionalHTMLElement
Element the canvas is appended to and sized from.
scale optionalnumber
Scene scale factor affecting camera distances and clipping. Default: 1
camPos optionalTHREE.Vector3
Initial camera position. Default: (10, 10, 10) ร— scale
targetPos optionalTHREE.Vector3
Initial look-at target. Default: (0, 0, 0)

๐Ÿ“ฆ Loading models

loadModel(url: string, loader?: THREE.Loader): Promise<Object3D>

Load a model and add it to the scene. Uses THREE.ObjectLoader by default; pass any loader (e.g. GLTFLoader) to override.

urlstring
URL of the model file.
loader optionalTHREE.Loader
Custom loader instance. Default: ObjectLoader
loadGLTF(url: string): Promise<Group>

Load a glTF/GLB and return a fresh clone of its scene, not added to the graph. Each URL is fetched and parsed once and cached, so repeated calls are cheap. Ideal for instancing static props. Position it and add it yourself, then call applyShadows().

Clones use Object3D.clone(), which does not duplicate skeletons. For animated/rigged models use loadAnimatedModel instead.
loadAnimatedModel(url: string, options?: AnimatedModelOptions): Promise<AnimatedModel>

Load a rigged glTF, add it to the scene, apply shadows, build an AnimationMixer and wire it into the render loop. Returns a handle to control playback. The render loop keeps drawing while a clip plays.

urlstring
URL of the .glb/.gltf file.
options optionalAnimatedModelOptions
Add-to-scene, shadows and autoplay settings.
const robot = await scene.loadAnimatedModel("/robot.glb", { autoplay: "Idle" });
robot.play("Walking");                 // cross-fade to a looping clip
robot.play("Wave", { loop: false }); // play once, hold last frame
robot.stop();                          // fade the current clip out

๐Ÿ’ก Lighting & scene

addLighting(options?: LightingOptions): this

Add a ready-to-use rig: a hemisphere fill, a shadow-casting key light, an opposite fill light, soft shadows and ACES Filmic tone mapping. Every part is configurable and has sensible defaults. Adds lights without clearing existing ones, so call it once per scene.

applyShadows(cast?: boolean, receive?: boolean): this

Flag every mesh currently in the scene as a shadow caster and/or receiver. Call it after adding your meshes (and after addLighting() with shadows on).

cast optionalboolean
Whether meshes cast shadows. Default: true
receive optionalboolean
Whether meshes receive shadows. Default: true
addSkybox(url?: string, color?: ColorRepresentation, name?: string): this

Add a 360ยฐ background. Pass a panoramic image URL, or omit it and pass a solid color.

url optionalstring
URL of a 360ยฐ image texture.
color optionalColorRepresentation
Fallback/solid color. Default: "#B2FFFF"
name optionalstring
Name assigned to the skybox object.

๐ŸŽฎ Controls & camera

addControls(overrides?: OrbitControlsConfig): this

Attach OrbitControls with damping and sensible limits. Any OrbitControls property can be overridden, including autoRotate, enableZoom, enablePan and the distance/angle limits.

moveCamera(position: Vector3, target?: Vector3, callback?: () => void): this

Animate the camera (and optionally the orbit target) to a new position over ~3 seconds. Controls are disabled during the move and restored when it finishes.

positionTHREE.Vector3
Target camera position.
target optionalTHREE.Vector3
New orbit target to tween toward.
callback optional() => void
Called when the move completes.
resetCameraPosition(): this

Animate the camera back to its initial position and target.

๐ŸŽฌ Animation

Property tweens are powered by tween.js (MIT) and target objects by their name.

animateModelColor(name: string, color: string | number, duration?: number): this

Tween every mesh material color under the named object toward a target color.

namestring
Name of the object in the scene.
colorstring | number
Target color (hex, name, or numeric).
duration optionalnumber
Seconds. Default: 2
animateModelPosition(name: string, position: Vector3, duration?: number): this

Tween the named object's position toward a target vector.

namestring
Name of the object in the scene.
positionTHREE.Vector3
Target position.
duration optionalnumber
Seconds. Default: 2
animateModelOpacity(name: string, value: number, duration?: number): this

Tween the named object's material opacity (materials are set transparent automatically).

namestring
Name of the object in the scene.
valuenumber
Target opacity, 0 to 1.
duration optionalnumber
Seconds. Default: 2
addMixer(mixer: THREE.AnimationMixer): this  ยท  removeMixer(mixer): this

Register (or unregister) an AnimationMixer so it is advanced automatically every frame. The render loop keeps drawing while any mixer is registered. loadAnimatedModel() does this for you.

๐Ÿงฒ Physics

Optional rigid-body simulation powered by cannon-es (MIT). Enable it once, then link meshes to bodies; the world is stepped and synced every frame.

enablePhysics(options?: PhysicsOptions): Promise<this>

Load cannon-es on demand and create the CANNON.World with the given gravity and default contact material. This is async: await it before adding bodies. No-op if physics is already enabled.

await scene.enablePhysics({ gravity: [0, -12, 0], restitution: 0.4 });
scene.addGround();
const body = scene.addBody(mesh, { mass: 1, shape: "box" });
addBody(mesh: Object3D, options?: PhysicsBodyOptions): CANNON.Body

Give a mesh a rigid body and keep the two in sync each frame. The collision shape is derived from the mesh's bounding box (or sphere) and its scale. Requires enablePhysics() first.

meshTHREE.Object3D
Mesh whose transform is driven by the body.
options optionalPhysicsBodyOptions
Mass, shape and damping.
addGround(y?: number): CANNON.Body

Add a static, infinite horizontal ground plane.

y optionalnumber
Height of the plane. Default: 0
removeBody(body: CANNON.Body): this

Remove a body from the world and stop syncing its mesh.

The underlying world is exposed as scene.physicsWorld for constraints, custom materials and collision events.

๐ŸŽฏ Picking

Raycast-based interaction with click-vs-drag discrimination, so orbiting never fires a stray click. Tune the threshold (pixels) via scene.clickDragThreshold (default 6).

enablePicking(onClick?, onHover?, onContextMenu?): this

Enable pointer picking and register callbacks. Hover fires with the object or null; click and right-click fire with the picked object.

onClick optional(obj: Object3D) => void
Called on a click (not a drag).
onHover optional(obj: Object3D | null) => void
Called when the hovered object changes.
onContextMenu optional(obj: Object3D) => void
Called on right-click.
disablePicking(): this

Disable picking and detach its listeners.

getSelectedObject(): Object3D | null

Return the currently hovered/selected object, or null.

pickAt(mouseX: number, mouseY: number): PickingResult | null

Programmatically raycast at normalized device coordinates (each in the range -1 to 1) and return the closest hit.

mouseXnumber
Normalized X, -1 (left) to 1 (right).
mouseYnumber
Normalized Y, -1 (bottom) to 1 (top).

โšก Rendering & lifecycle

The loop is render-on-demand: a frame is drawn only when the camera moves, a tween/mixer/physics body is active, or an additional render function is set.

startRenderLoop(): this  ยท  stopRenderLoop(): this

Start or stop the requestAnimationFrame loop. The loop starts automatically when a container is attached.

setAdditionalRenderFn(fn: () => void): this

Register a callback run every frame (e.g. for custom per-frame logic). Setting it also keeps the loop rendering continuously.

attachRenderer(container: HTMLElement): this

Append the canvas to a container, size it, and start the render loop. Called for you when a container is passed to the constructor.

dispose(): void

Stop rendering, clear tweens, mixers and physics, dispose the renderer and remove the canvas. Call it when you are done to avoid leaks.

๐Ÿ”ง Properties

SceneCreator is a thin layer: the raw Three.js objects stay accessible for anything the helpers don't cover.

sceneTHREE.Scene
The scene graph. Add your own objects with scene.scene.add(...).
cameraTHREE.PerspectiveCamera
The active camera.
rendererTHREE.WebGLRenderer
The WebGL renderer (antialiased, alpha enabled).
controlsOrbitControls | undefined
Present after addControls().
physicsWorldCANNON.World | undefined
Present after enablePhysics().
clickDragThresholdnumber
Click-vs-drag pixel threshold. Default: 6
scalenumber
Scene scale factor. Default: 1

๐Ÿ“ Types

interface LightingOptions
hemispherefalse | { sky?, ground?, intensity? }
Sky/ground ambient fill. false to omit.
keyDirectionalLightSpec
Main shadow-casting light.
fillfalse | DirectionalLightSpec
Opposite fill light. false to omit.
shadowsboolean
Enable shadow mapping. Default: true
shadowAreanumber
Half-size of the shadow frustum. Default: 16
shadowMapSizenumber
Shadow map resolution. Default: 2048
toneMappingboolean
ACES Filmic tone mapping. Default: true
exposurenumber
Tone mapping exposure. Default: 1.05
interface DirectionalLightSpec
colorColorRepresentation
Light color.
intensitynumber
Light intensity.
positionTHREE.Vector3
Light position.
interface OrbitControlsConfig

A subset of OrbitControls properties (enableDamping, dampingFactor, minDistance, maxDistance, maxPolarAngle, enableZoom, enablePan, rotateSpeed, ...) plus any extra key, all assigned onto the controls instance.

interface AnimatedModel
modelTHREE.Group
The loaded model root.
animationsAnimationClip[]
Parsed clips.
namesstring[]
Names of every clip.
mixerTHREE.AnimationMixer
The driving mixer (already wired in).
actionsRecord<string, AnimationAction>
Actions keyed by clip name.
play(name, options?)(...) => AnimationAction | null
Cross-fade to a clip. See PlayAnimationOptions.
stop(fade?)(fade?: number) => void
Fade the current clip out.
interface AnimatedModelOptions
addboolean
Add the model to the scene. Default: true
shadowsboolean
Flag meshes as shadow casters/receivers. Default: true
autoplaystring | false
Clip to start (or false for none). Default: first clip
interface PlayAnimationOptions
fadenumber
Cross-fade duration (seconds). Default: 0.3
loopboolean
Loop the clip. Default: true
clampWhenFinishedboolean
Hold the last frame of a non-looping clip. Default: true
interface PhysicsOptions
gravityVector3 | [number, number, number]
World gravity. Default: [0, -9.82, 0]
restitutionnumber
Default contact bounciness, 0 to 1. Default: 0.3
frictionnumber
Default contact friction. Default: 0.4
allowSleepboolean
Let resting bodies sleep. Default: true
interface PhysicsBodyOptions
massnumber
Mass in kg; 0 is static. Default: 1
shape"box" | "sphere"
Collision shape from the mesh. Default: "box"
linearDampingnumber
Linear damping. Default: 0.01
angularDampingnumber
Angular damping. Default: 0.01
interface PickingResult
objectTHREE.Object3D
The intersected object.
distancenumber
Distance from the camera.
pointTHREE.Vector3
World-space hit point.
normalTHREE.Vector3
Surface normal at the hit.
uvTHREE.Vector2 | undefined
UV coordinates, if available.