3D · guides · Getting Started
Porting from Three.js
Most of a Three.js port is renaming. The hard part is code that still compiles and runs after the rename but draws a darker scene, a sideways light or a floor that casts a shadow. This page lists those differences first, then ports a small scene line by line. The API reference covers every name. The pixijs-3d agent skill that ships in the npm package also has full tables of Three.js names.
API mapping
- The scene is one display object in a PixiJS app. There's no renderer to own. Create an
Application, add aView3Dto its stage, and build the scene underview3d.root. 2D sprites, text and UI share the canvas, as View3D and 2D content explains. - The view holds the scene-wide settings that Three.js splits between the renderer and the scene:
toneMapping,exposure,shadowQuality,fog,background,environmentand the post-processingpasses. - Constructors take one options object, such as
new CubeGeometry({ width, height, depth }). The same values are properties on the object afterwards. - The frame loop is
app.ticker. Its callback gets the ticker.OrbitCamera, a model'splayerandParticleSystem3Dadvance onTicker.sharedunless you step them yourself. - A loaded glTF is a template.
Assets.loadreturns aScene3DSource, and eachModel3Dbuilt from it shares its geometry, materials and textures. See Loading models.
Behaviour differences
Each line compiles after a plain rename and still changes what you see. The first group affects nearly every port.
Light and shadow
- A HemisphereLight becomes the view's environment, a
Hemisphere. Three.js divides its intensity by π and the view doesn't, so passintensity: i / Math.PI. Directional, point, spot and ambient intensities port unchanged. - Lights have no target. Directional and spot lights shine along their local -Z axis, so call
lookAtafter placing one, and again after every move (Lights). An unrotatedDirectionalLightshines sideways, not down. - Meshes and models cast and receive shadows
by default. SetcastShadow: falseon floors and terrain, which otherwise also stretch the sun's shadow box. shadow.biashas the opposite sign, so negate a Three.js value or keep the default.shadow.mapSizebecomes the view'sshadowQuality, and the shadow camera's box becomesshadow.bounds, which is in world space.
Time
ticker.deltaTimemeasures time in 60 fps frames, not seconds. For the seconds that Three.js's getDelta() returns, useticker.deltaMS / 1000. The ticker capsdeltaMSat 100 ms, so below 10 fps motion slows down.- Where Three.js called
controls.update()ormixer.update(dt)in the loop, build the controller or model withautoUpdate: false. Then call itsupdate(ticker)fromapp.ticker, so it steps in the same frame as your code. - The camera defaults to a field of view of 60 and a far plane of 1000 (
Camera3D.fov,Camera3D.far). Three.js apps usually pass 50 and 2000, so set both.
Colour and output
- Tone mapping is off by default in both engines.
exposurescales the light even under'none', where Three.js ignores it, so port a NoToneMapping app with exposure 1. scene.backgroundbecomes the view'sbackground, which post effects and fog cover as they do in Three.js. Under a tone mapping curve such as'aces', the background goes through the curve too. For an exact colour, passbackgroundColortoapp.initinstead.- An emissive colour is an opt-in group,
emissive: { color, strength }, and its colour defaults to white. Pass the group only where the Three.js emissive isn't black.
Surfaces
alphaModedefaults to'opaque', which ignoresalphaand texture alpha.transparent: truebecomes'blend', and alphaTest becomes'mask'withalphaCutoff.- Images upload without Three.js's
flipY, so a picture on aPlaneGeometryneedsflipV: true(Textures on the built-in shapes). - Mipmaps are off by default. Load a tiled or distant texture with
Assets.load({ src, data: { autoGenerateMipmaps: true } }), or it shimmers. - Texture
repeatandoffsetbecome the material'suvTransform, a PixiJSMatrixthat applies to all its textures. Base colour and emissive textures always decode as sRGB, socolorSpacehas nothing to port to.
Scene graph
rotationandquaterniondon't stay in sync. Nodes from a glTF file, and nodes you've calledlookAton, store their orientation in the quaternion, sorotation.y += athrows it away (Rotation and quaternion).getLocalBoundsmeasures in the node's own space, without its own transform, where Box3.setFromObject measures in world space.- A Three.js crossFadeTo becomes two playing states whose
weightsyou ramp, andplay()doesn't stop any other state (Blending and crossfades).
Side-by-side example
A Three.js scene with a hemisphere light, a shadowed sun, a ground, a spinning box and orbit controls:
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x9cc7ea);
const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(4, 3, 8);
const controls = new OrbitControls(camera, renderer.domElement);
scene.add(new THREE.HemisphereLight(0xdfeeff, 0x5a5040, 1.5));
const sun = new THREE.DirectionalLight(0xfff4e0, 2.5);
sun.position.set(4, 8, 5);
sun.castShadow = true;
scene.add(sun);
const ground = new THREE.Mesh(new THREE.PlaneGeometry(20, 20), new THREE.MeshStandardMaterial({ color: 0x7a9a5a }));
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
const box = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshStandardMaterial({ color: 0xd9534f }));
box.position.y = 0.5;
box.castShadow = true;
scene.add(box);
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
const timer = new THREE.Timer();
renderer.setAnimationLoop((time) => {
timer.update(time);
box.rotation.y += timer.getDelta();
controls.update();
renderer.render(scene, camera);
});
Here's the same scene in PixiJS 3D. Comments mark each line that's more than a rename.
import { CubeGeometry, DirectionalLight, Material3D, Mesh3D, PlaneGeometry, View3D } from '@pixi/3d';
import { OrbitCamera } from '@pixi/3d/extras';
import { Application } from 'pixi.js';
const app = new Application();
await app.init({
resizeTo: window,
antialias: true,
resolution: Math.min(window.devicePixelRatio, 2),
autoDensity: true,
});
document.body.appendChild(app.canvas);
const view3d = new View3D({
autoResize: true, // follows the app's size, so you need no resize listener
background: 0x9cc7ea,
environment: { skyColor: 0xdfeeff, groundColor: 0x5a5040, intensity: 1.5 / Math.PI }, // divide by π
});
app.stage.addChild(view3d);
view3d.camera.fov = 50; // the default is 60
view3d.camera.far = 100;
view3d.camera.position.set(4, 3, 8); // before the controller, which keeps this offset
const orbit = new OrbitCamera({ view: view3d, autoUpdate: false });
const sun = new DirectionalLight({ color: 0xfff4e0, intensity: 2.5, castShadow: true, position: { x: 4, y: 8, z: 5 } });
view3d.root.addChild(sun);
sun.lookAt({ x: 0, y: 0, z: 0 }); // lights have no target, so aim it
const ground = new Mesh3D({
geometry: new PlaneGeometry({ width: 20, height: 20 }),
material: new Material3D({ baseColor: 0x7a9a5a }),
rotation: { x: -Math.PI / 2, y: 0, z: 0 }, // lies in XY like Three.js's, so lay it flat
castShadow: false, // meshes cast shadows by default
});
view3d.root.addChild(ground);
const box = new Mesh3D({
geometry: new CubeGeometry({ width: 1, height: 1, depth: 1 }),
material: new Material3D({ baseColor: 0xd9534f }),
position: { x: 0, y: 0.5, z: 0 },
});
view3d.root.addChild(box);
app.ticker.add((ticker) => {
box.rotation.y += ticker.deltaMS / 1000; // deltaTime counts frames
orbit.update(ticker); // stepped here, like controls.update()
});
Next steps
- Quick start: the same pieces built up one step at a time.
- Environment and IBL: tone mapping, exposure, fog and cube maps.
- Shadows: the shadow box, cascades and fixing artefacts.
- Materials: choosing between the physically based, Phong, flat and toon materials.