Skip to content

Batch Rendering

SpriteGroup automatically batches sprites that share the same material into single draw calls. You don’t need to do anything special—just add sprites and the batching happens behind the scenes.

How It Works

When you add sprites to a SpriteGroup, they’re grouped by material and layer into efficient GPU batches. Systems run automatically during Three.js rendering — no manual update() call needed.

import { Sprite2D, SpriteGroup, Sprite2DMaterial } from 'three-flatland';
const spriteGroup = new SpriteGroup();
scene.add(spriteGroup);
// These sprites share a material, so they batch together
const material = new Sprite2DMaterial({ map: spriteSheet });
for (let i = 0; i < 1000; i++) {
const sprite = new Sprite2D({ material });
sprite.position.set(Math.random() * 100, Math.random() * 100, 0);
spriteGroup.add(sprite);
}
// In your render loop — no update() call needed
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}

Renderer Options

The SpriteGroup constructor accepts optional batch configuration:

const spriteGroup = new SpriteGroup({
maxBatchSize: 8192, // Max sprites per batch (default: 8192)
autoSort: true, // Enable automatic sorting (default: true)
frustumCulling: true, // Enable frustum culling (default: true)
autoInvalidateTransforms: true, // Auto-sync transforms every frame (default: true)
});
scene.add(spriteGroup);

Automatic Change Detection

Property changes on sprites are detected automatically through the ECS. When you update a sprite’s tint, alpha, flip, frame, layer, or position, the renderer syncs only the changed data to the GPU on the next frame. No manual invalidation needed.

// Just change properties — the renderer handles the rest
sprite.tint = [1, 0, 0];
sprite.alpha = 0.5;
sprite.layer = Layers.UI;
sprite.position.x += 10;

Performance Monitoring

Check batch efficiency with the stats property:

const stats = spriteGroup.stats;
console.log(`Sprites: ${stats.spriteCount}`);
console.log(`Batches: ${stats.batchCount}`);
console.log(`Draw calls: ${stats.drawCalls}`);
console.log(`Visible: ${stats.visibleSprites}`);

Goal: Keep batchCount low relative to spriteCount. If you have 1000 sprites but 100 batches, you’re not batching efficiently.

Optimization Tips

Share Materials

Sprites only batch together when they share the same material instance:

// Good: One material, one batch
const material = new Sprite2DMaterial({ map: atlas });
sprites.forEach(s => s.material = material);
// Bad: Many materials, many batches
sprites.forEach(s => {
s.material = new Sprite2DMaterial({ map: atlas }); // Creates new batch each time!
});

Use Texture Atlases

Combine textures into a single atlas. All sprites using the atlas can share one material:

// Load a sprite sheet with frame data
const { texture, frames } = await SpriteSheetLoader.load('/sprites/atlas.json');
const material = new Sprite2DMaterial({ map: texture });
// Different frames, same material = same batch
const player = new Sprite2D({ material, frame: frames.get('player') });
const enemy = new Sprite2D({ material, frame: frames.get('enemy') });

Organize by Layer

Sprites are sorted by layer before batching. Keep related sprites on the same layer:

import { Layers } from 'three-flatland';
// Background sprites batch together
backgrounds.forEach(s => s.layer = Layers.BACKGROUND);
// Entity sprites batch together
entities.forEach(s => s.layer = Layers.ENTITIES);

Next Steps

  • Flatland Guide — Wrap SpriteGroup with camera, post-processing, and global uniforms
  • Sprites Guide — Sprite2D properties and configuration