Flatland is the high-level entry point for three-flatland. It wraps a SpriteGroup with an orthographic camera, global uniforms, post-processing pipeline, and render target support into a single object.
If you’re new and not sure whether you need Flatland or SpriteGroup — start here. The table below tells you when to graduate.
When to Use Flatland vs SpriteGroup
Section titled “When to Use Flatland vs SpriteGroup”| Flatland | SpriteGroup | |
|---|---|---|
| Use case | Self-contained 2D pipeline | Embed sprites into your own scene |
| Camera | Internal orthographic camera | You provide the camera |
| Post-processing | Built-in pass effect pipeline | Not included |
| Global uniforms | Automatic time, viewport, tint | Not included |
| Render call | flatland.render(...) | renderer.render(...) |
Use Flatland when you want a complete 2D rendering setup. Use SpriteGroup when you need to mix sprites into an existing 3D scene with your own camera and render loop.
Basic Setup
Section titled “Basic Setup”import { WebGPURenderer } from 'three/webgpu'import { Flatland, Sprite2D, TextureLoader } from 'three-flatland'
const renderer = new WebGPURenderer()await renderer.init()document.body.appendChild(renderer.domElement)
const flatland = new Flatland({ viewSize: 400, clearColor: 0x1a1a2e })const texture = await new TextureLoader().loadAsync('/sprites/hero.png')
flatland.add(new Sprite2D({ texture }))
function animate() { flatland.resize(window.innerWidth, window.innerHeight) flatland.render(renderer) requestAnimationFrame(animate)}animate()import { useRef, useEffect } from 'react'import { Canvas, extend, useFrame, useThree } from '@react-three/fiber/webgpu'import { Flatland, Sprite2D, Sprite2DMaterial } from 'three-flatland/react'import type { Flatland as FlatlandType } from 'three-flatland/react'import type { WebGPURenderer } from 'three/webgpu'
extend({ Flatland, Sprite2D, Sprite2DMaterial })
function Scene() { const flatlandRef = useRef<FlatlandType>(null) const { renderer, size } = useThree()
useEffect(() => { flatlandRef.current?.resize(size.width, size.height) }, [size.width, size.height])
// Render in the 'render' phase so R3F skips its own render useFrame(() => { flatlandRef.current?.render(renderer as unknown as WebGPURenderer) }, { phase: 'render' })
return ( <flatland ref={flatlandRef} viewSize={400} clearColor={0x1a1a2e}> <sprite2D texture={texture} /> </flatland> )}
export default function App() { return ( <Canvas renderer={{ antialias: false }}> <Scene /> </Canvas> )}Constructor Options
Section titled “Constructor Options”const flatland = new Flatland({ viewSize: 400, // Orthographic view height in world units clearColor: 0x1a1a2e, // Background color clearAlpha: 1, // Background alpha (< 1 for transparent) autoClear: true, // Clear before each render postProcessing: false, // Enable post-processing pipeline aspect: 1, // Initial aspect ratio (use resize() to update) camera: null, // Custom OrthographicCamera (null = internal) renderTarget: null, // RenderTarget (null = render to viewport)})See the FlatlandOptions API reference for full type details.
Adding Objects
Section titled “Adding Objects”flatland.add() routes objects automatically:
- Sprite2D instances go to the internal
SpriteGroupfor batched rendering - Light2D instances are tracked for the lighting system
- Other Object3D instances are added directly to the internal scene
// Sprite2D → batched via SpriteGroupconst sprite = new Sprite2D({ texture, anchor: [0.5, 0.5] })flatland.add(sprite)
// Light2D → tracked by lighting systemconst light = new Light2D({ type: 'point', position: [50, 50], color: 0xff6600 })flatland.add(light)
// Other Three.js objects → added to internal sceneconst mesh = new Mesh(geometry, material)flatland.add(mesh)Global uniforms are automatically wired to each sprite’s material on add().
Lighting
Section titled “Lighting”Flatland manages 2D lighting with Light2D sources and a LightEffect pipeline. Activate lighting by calling setLighting() with a preset:
import { DefaultLightEffect } from '@three-flatland/presets'
flatland.setLighting(new DefaultLightEffect())All sprites receive lighting by default. Set lit: false to opt out:
const indicator = new Sprite2D({ texture, lit: false }) // always full brightness| Method / Property | Description |
|---|---|
setLighting(effect) | Set the active LightEffect (or null to disable) |
lighting | Current LightEffect (read-only) |
lights | All tracked Light2D instances (read-only) |
See the 2D Lighting guide for preset comparisons, Light2D types, and custom effects.
Render Loop
Section titled “Render Loop”Each frame, call resize() to sync the aspect ratio, then render() to draw:
function animate() { flatland.resize(canvas.width, canvas.height) flatland.render(renderer) requestAnimationFrame(animate)}resize() updates the internal camera frustum and render target dimensions. render() syncs global uniforms (time, viewport), updates sprite batches, and draws everything.
Global Uniforms
Section titled “Global Uniforms”Every Flatland instance exposes a globals object with shared uniforms. These update once per frame and are available to all sprite materials via TSL nodes.
| Uniform | Description |
|---|---|
time | Elapsed seconds (undefined = auto) |
globalTint | Color tint for all sprites |
viewportSize | Viewport size in pixels |
pixelRatio | Device pixel ratio |
wind | Wind direction and strength |
fogColor | Fog color |
fogRange | Fog near/far range |
Auto vs Manual Time
Section titled “Auto vs Manual Time”By default, time is undefined and Flatland accumulates elapsed time automatically. Set it to a number for manual control:
// Auto mode (default) — time accumulates each frameflatland.globals.time = undefined
// Manual mode — set exact valueflatland.globals.time = performance.now() / 1000Using Uniforms in TSL
Section titled “Using Uniforms in TSL”Access the TSL node directly for custom material effects:
import { Sprite2DMaterial } from 'three-flatland'
const material = new Sprite2DMaterial({ colorTransform: (ctx) => { // Use the global tint node in a custom effect const tinted = ctx.color.rgb.mul(flatland.globals.globalTintNode) return tinted.toVec4(ctx.color.a) }})Each global has a corresponding TSL node: timeNode, globalTintNode, viewportSizeNode, pixelRatioNode, windNode, fogColorNode, fogRangeNode.
Statistics
Section titled “Statistics”Monitor batching with flatland.spriteGroup.stats:
const stats = flatland.spriteGroup.stats
console.log(`Sprites: ${stats.spriteCount}`)console.log(`Batches: ${stats.batchCount}`)console.log(`Visible: ${stats.visibleSprites}`)Draw calls aren’t part of stats — read them from the renderer after a frame: renderer.info.render.calls reflects actual GPU work.
Post-Processing
Section titled “Post-Processing”Add full-screen pass effects with addPass():
import { Flatland, createPassEffect } from 'three-flatland'import { posterize } from '@three-flatland/nodes'
const PosterizePass = createPassEffect({ name: 'posterize', schema: { bands: 6 }, pass: ({ uniforms }) => (input, uv) => posterize(input, uniforms.bands),})
const flatland = new Flatland({ viewSize: 400 })const post = new PosterizePass()flatland.addPass(post)
// Zero-cost parameter updatespost.bands = 10The render pipeline auto-initializes on the first addPass() call. Passes chain in insertion order — each receives the previous pass’s output.
| Method | Description |
|---|---|
addPass(pass, order?) | Add a pass effect |
removePass(pass) | Remove a specific pass |
clearPasses() | Remove all passes |
passes | Read-only array of current passes |
See the Pass Effects guide for creating custom effects and the full node library.
Render to Texture
Section titled “Render to Texture”Render Flatland output to a texture for use in 3D scenes:
import { RenderTarget } from 'three'
const target = new RenderTarget(512, 512)const flatland = new Flatland({ renderTarget: target })
// Use the texture on a 3D meshmesh.material.map = flatland.texture
// Each frame: render 2D first, then 3Dflatland.render(renderer)renderer.render(scene3D, camera3D)Disposal
Section titled “Disposal”Clean up all resources when done:
flatland.dispose()This destroys the ECS world, sprite batches, render pipeline, and all pass effect entities.
Next Steps
Section titled “Next Steps”- 2D Lighting — Light2D sources and LightEffect presets
- Pass Effects — Full-screen post-processing effects
- Batch Rendering — How sprite batching works under the hood
- TSL Nodes — Per-sprite material effects and low-level TSL usage
- Breakout Showcase — A complete game built with Flatland