3D and 2D share one depth buffer. The camera is calibrated so z = 0 maps 1:1 onto canvas pixels: a box at x: 960, y: 540, z: 0 sits in the centre of a 1920×1080 frame. y points down, so “up” is −y.
The fast path: a lighting rig
Section titled “The fast path: a lighting rig”import { Composition, Layer3D, LayerAnimation, Lighting } from "framefields";
const W = 1920, H = 1080;const comp = new Composition({ width: W, height: H, fps: 30, durationMs: 4000, backgroundColor: "#0b0b0c" });
comp.add(Lighting.studio({ target: [W / 2, H / 2, 0] })); // environment + key/rim/fill, soft shadow
comp.add( Layer3D.text({ text: "framefields", fontFamily: "Archivo", fontWeight: 800, fontSize: 280, extrusionDepth: 26, fill: "#f5e300", bevelColor: "#6b6300", roughness: 0.35, x: W / 2, y: H / 2, z: 0, }).animate(LayerAnimation.create().keys("rotateY", [[0, -20], [120, 6, "sine.inOut"]])),);Rigs: Lighting.studio, soft, sunset, dramatic. Options: target, distance, rotation, intensity, exposure (stops), toneMapping, shadows (default on), shadowSoftness, fog, bloom. A rig returns an array; comp.add takes it, or spread it into children.
Camera
Section titled “Camera”import { CameraAnimation, Layer } from "framefields";
comp.add( Layer.camera({ x: 960, y: 540, z: -1000, targetX: 960, targetY: 540, targetZ: 0 }).animate( CameraAnimation.camera() .orbit({ azimuth: { from: -30, to: 30 }, elevation: { from: 15, to: 15 }, radius: { to: 1200 }, start: 0, end: 300, ease: "sine.inOut" }) .rackFocus({ fromDistance: 2000, toDistance: 1200, fromFStop: 1.8, toFStop: 4, start: 20, end: 60 }), ),);CameraAnimation.camera() moves: orbit, dolly, truck, pedestal, pan, tilt, roll, rackFocus, handheld({ translationAmplitude, rotationAmplitude }). Camera mode is "lookAt" (default), "free" or "orbit".
Lights by hand
Section titled “Lights by hand”import { Layer, Light } from "framefields";
comp.add([ Layer.environment("studio", { // presets: studio | soft | sunset | night | neutral exposure: 0.3, toneMapping: "neutral", // default; "agx" filmic, "aces" punchy, "none" rotation: 30, fog: { color: "#0b0b0c", start: 2000, end: 5000 }, bloom: true, // or { intensity, threshold, radius } }), Light.directional({ x: 400, y: -1200, z: -600, targetX: 960, targetY: 540, castShadow: true }), Light.ambient("#ffffff", 0.3),]);Light.ambient, Light.directional, Light.point, Light.spot (also Layer.ambientLight, Layer.directionalLight, Layer.pointLight, Layer.spotLight).
| Rule | Why |
|---|---|
Add Layer.environment(...) for roughness / metallic to mean anything |
Without it the scene uses classic Blinn-Phong (lint: pbr_without_environment) |
One castShadow light, directional or spot |
Only the first casts (multiple_shadow_lights); point/ambient can’t (shadow_unsupported_light) |
Tone mapping "neutral" keeps brand colors as authored |
"agx" rolls highlights off filmically |
| Bloom only takes light well past white | Use emissiveIntensity ≳ 3 to make something glow |
Set fog.color to the background |
Surfaces fade into it seamlessly |
Primitives
Section titled “Primitives”Layer3D.roundedBox({ x: 960, y: 540, z: 0, width: 300, height: 80, depth: 300, radius: 18, taper: 0.9, color: "#ff5a1f", roughness: 0.45 }); // keycapLayer3D.sphere({ x: 700, y: 540, z: 0, radius: 90, color: "#e8e8e8", metallic: 1, roughness: 0.15 }); // chromeLayer3D.cylinder({ x: 1200, y: 540, z: 0, radius: 12, height: 400, color: "#111", emissive: "#4da3ff", emissiveIntensity: 3 }); // neon tube| Builder | Makes |
|---|---|
Layer3D.roundedBox / sphere / cylinder |
solid smooth-shaded meshes; equal shapes share GPU buffers, so hundreds are cheap |
Layer3D.cube({ size, faces }) |
a cube whose faces are 2D layers |
Layer3D.carousel({ radius, items }) |
2D items on a ring |
Layer3D.prism, Layer3D.plane, Layer3D.grid |
prisms, flat cards, ground grids |
Layer3D.text / Layer3D.extrudedText |
extruded, bevelled type from your registered fonts |
Surface props on meshes: color, tint (animates as color), roughness, metallic, emissive, emissiveIntensity, castShadow. Flat layers cast shadows only with castShadow: true; meshes cast by default.
Models
Section titled “Models”comp.add(Layer.glb("assets/models/character.glb", { x: 960, y: 640, z: 0, scale: 2.5, loop: true }));Loaders: Layer.glb, gltf, obj, fbx, stl, ply, vox, threeDS, off (also on Layer3D). glTF brings base color, normal, metallic-roughness, occlusion and emissive maps, and skeletal animation (up to 128 bones). Animate playback with animationProgress, animationTime or animationSpeed.
Animatable 3D props
Section titled “Animatable 3D props”z, rotateX/Y/Z, scaleX/Y/Z, roughness, metallic, emissive, emissiveIntensity, exposure, intensity, plus camera props (cameraX/Y/Z, targetX/Y/Z, cameraFov, focusDistance, fStop, orbitRadius, orbitAzimuth, orbitElevation).