What you’ll learn
- How to create, position, anchor, and scale a
Sprite2D - How to opt sprites into the lighting and shadow pipeline (
lit,castsShadow,shadowRadius) - How layers and z-index control render order independently of scene-graph order
The Sprite2D class is the foundation for 2D rendering in three-flatland. If your scene only ever needs static images on screen, this is the only class you need to learn.
import { Sprite2D, SpriteGroup } from 'three-flatland';
const sprite = new Sprite2D({ texture: myTexture, anchor: [0.5, 0.5], // Center anchor});
spriteGroup.add(sprite);import { extend } from '@react-three/fiber/webgpu';import { Sprite2D, SpriteGroup } from 'three-flatland/react';
// Register with R3F (required once)extend({ Sprite2D, SpriteGroup });
function Scene() { return ( <spriteGroup> <sprite2D texture={myTexture} anchor={[0.5, 0.5]} /> </spriteGroup> );}Properties
Section titled “Properties”Position
Section titled “Position”Sprites inherit from Three.js Mesh and support standard transform properties:
sprite.position.set(100, 200, 0);sprite.rotation.z = Math.PI / 4; // 45 degree rotationsprite.scale.set(2, 2, 1); // 2x scaleAnchor
Section titled “Anchor”The anchor point determines the sprite’s origin for positioning and rotation. Values range from 0 to 1.
| Anchor | Description |
|---|---|
[0, 0] | Bottom-left corner |
[0.5, 0.5] | Center (default) |
[0.5, 0] | Bottom-center |
[1, 1] | Top-right corner |
Sprite geometry is a unit plane — display size comes from scale. Set scale.set(width, height, 1) in pixels:
const sprite = new Sprite2D({ texture: myTexture });sprite.scale.set(64, 64, 1);To sample a sub-region of a texture atlas, pass a frame from a SpriteSheet:
const sprite = new Sprite2D({ texture: sheet.texture, frame: sheet.getFrame('player_idle_0'),});SpriteGroup
Section titled “SpriteGroup”For batched rendering with many sprites, use SpriteGroup:
import { SpriteGroup } from 'three-flatland';
const spriteGroup = new SpriteGroup();scene.add(spriteGroup);
spriteGroup.add(sprite1);spriteGroup.add(sprite2);
// In animation loop — no update() call neededrenderer.render(scene, camera);import { extend } from '@react-three/fiber/webgpu';import { Sprite2D, SpriteGroup } from 'three-flatland/react';
extend({ Sprite2D, SpriteGroup });
function Scene() { return ( <spriteGroup> <sprite2D texture={texture1} /> <sprite2D texture={texture2} /> </spriteGroup> );}For simple cases with few sprites, you can add them directly to the scene without SpriteGroup.
Lighting and shadows
Section titled “Lighting and shadows”Sprites integrate with the active LightEffect and the SDF shadow pipeline through four properties. All four are also accepted as constructor options and as JSX props on <sprite2D>.
| Property | Type | Default | Purpose |
|---|---|---|---|
lit | boolean | true | Receive lighting from the attached LightEffect. |
receiveShadows | boolean | true | Allow lit fragments to be darkened by the SDF shadow trace. |
castsShadow | boolean | false | Contribute the sprite’s silhouette to the SDF occlusion pre-pass. |
shadowRadius | number | undefined | undefined (auto) | Per-instance occluder radius in world units. |
const hero = new Sprite2D({ texture, castsShadow: true })hero.lit = true // (default) reacts to lightshero.receiveShadows = false // opt out of being darkened by shadow zonesIn React:
<sprite2D texture={myTexture} castsShadow lit receiveShadows={false}/>lit, receiveShadows, and castsShadow are bit flags packed into instanceSystem.z of the interleaved instance buffer (see Batch Rendering for material authors).
shadowRadius
Section titled “shadowRadius”shadowRadius is the world-space size of the sprite as a shadow occluder. It’s consumed by SDF shadow tracers as the self-silhouette escape distance — the value the trace steps “out of” when the sample point starts on top of the caster.
When left undefined (the default), transformSyncSystem auto-derives it each frame from max(|scale.x|, |scale.y|) so it tracks scale changes (including animation frames whose source sizes differ). Set it explicitly when:
- The visible body is tighter than the quad’s bounds (e.g., a sprite with transparent padding shouldn’t use its full bounding-box scale as the silhouette radius).
- The anchor pushes the silhouette off-center and you need a different effective size.
sprite.shadowRadius = 12 // overridesprite.shadowRadius = undefined // back to autoLayers
Section titled “Layers”Use the Layers enum to control render order:
import { Layers } from 'three-flatland';
sprite.layer = Layers.BACKGROUND; // Renders behind other spritessprite.layer = Layers.ENTITIES; // Normal render order for game entitiessprite.layer = Layers.FOREGROUND; // Renders in frontsprite.layer = Layers.UI; // UI layer (renders last)Available layers (in render order):
Layers.BACKGROUND- Background elementsLayers.GROUND- Ground/terrainLayers.SHADOWS- Shadow spritesLayers.ENTITIES- Game entities (default)Layers.EFFECTS- Particles and effect spritesLayers.FOREGROUND- Foreground elementsLayers.UI- User interface
Next steps
Section titled “Next steps”- Basic Sprite example — texture loading, anchor, and interaction in a runnable scene.
Sprite2DAPI reference — every constructor option and property.- Animation for spritesheet playback, and Batch Rendering for thousands of sprites in a few draw calls.