The camera whose view of the scene fills this rectangle (§48).
OptionalclearColour this viewport's rectangle is cleared to before drawing, as RGBA
components in [0, 1] with straight (non-premultiplied) alpha. Absent
means the renderer does not clear colour for this view — which is how a
picture-in-picture or minimap view draws over what the main view already
rendered.
§48 types this as Color, the engine-wide colour type that also accepts
CSS-style strings. That type lands with @fourjs/materials (§59, §60a), and
@fourjs/scene may not depend on @fourjs/materials (plan §3.1 pins the
dependency matrix and forbids adding edges), so the field is a numeric RGBA
tuple here (decision, WP-3.2). Components are linear-light, matching the
§60a pipeline: a CSS string denotes sRGB and would need decoding, which is
exactly the conversion Color will own. The tuple is a valid Color input
when that type arrives, so widening the field then is source-compatible.
Height, in pixels — or a fraction of the surface height if normalized.
Stable identity, unique within one render call (§48). Renderers key per-view state by it and diagnostics name views by it; it is not a scene node id.
OptionallayerWhich §46 layers this view draws, narrowing (or replacing) its camera's own
layers (§48, R-38, 2026-08-08).
const world = { ...createFullscreenViewport(camera), layerMask: layerMask("default") };
const ui = { ...createFullscreenViewport(camera, "ui"), layerMask: layerMask("ui") };
renderer.render(scene, [world, ui]); // one camera, two passes, no overdraw
Absent means the camera decides: the effective mask for a view is
view.layerMask ?? view.camera.layers, and Camera.layers defaults to
ALL_LAYERS, so a viewport that says nothing draws everything — exactly as
every viewport did before this field existed. A node is drawn into this view
when layersMatch(node.layers, effectiveMask).
Per-view rather than per-camera because that is the split §47 and §48 make: a camera's mask is what that camera can see at all, a viewport's is what this rectangle shows of it. Split-screen with a shared overlay, a minimap that hides debug gizmos, and a screen-space UI pass over a world pass are all the same one-camera, two-viewports arrangement.
Honoured by the backend per view, from one shared render list: a filtered item is skipped before any GPU resource is touched. When the per-view render list arrives (R-8) the filter moves to list-construction time — the field's meaning does not change.
OptionalnormalizedWhen true, the rectangle is expressed as fractions of the target surface in
[0, 1] instead of pixels (§48). Absent means false — pixels.
Width, in pixels — or a fraction of the surface width if normalized.
Left edge, in pixels — or a fraction of the surface width if normalized.
Bottom edge, in pixels — or a fraction of the surface height if normalized.
A camera rendered into a rectangular region of a surface (§48).
The rectangle
x,y,width,heightare pixels by default, or fractions of the target surface whennormalizedis true — so a full-screen viewport is{ x: 0, y: 0, width: 1, height: 1, normalized: true }and stays correct across resizes, while a fixed 256 px minimap is not normalized. The origin is the bottom-left corner of the target and +Y is up, matching the world convention (§7a) and the clip-space convention of the D8 projections; a backend whose native scissor rectangle is top-left based flips it on the way in.Deferred §48 fields
§48's full interface additionally declares
clearDepth,layerMask,renderTarget, andpostProcessing.layerMasklanded with §46's layer registry (R-38, 2026-08-08) and is documented below; of the rest:renderTarget?: RenderTarget— off-screen targets are §63/§65 work (offscreen textures, mirrors, portals, 3D previews inside 2D UI); the MVP renders to the canvas only.postProcessing?: RenderPipeline— the §63 render graph.clearDepth?: number— meaningful once depth clearing is configurable per view; the MVP clears depth to 1 for every view.They are added to this interface, with the same names, by the packets that implement them. Adding an optional property to an interface is backwards-compatible, so nothing here has to be re-shaped for that.