TSL Nodes
What This Example Shows
This example demonstrates the Material Effect system — the recommended way to create per-sprite shader effects. It showcases 8 visual modes on an animated sprite:
| Effect | Description |
|---|---|
| Normal | No effect — base sprite rendering |
| Damage Flash | Additive white flash that fades out |
| Dissolve | Pixelated noise-based dissolve |
| Powerup | Continuous hue rotation (rainbow) |
| Petrify | Desaturation to grayscale |
| Select | Green outline around sprite |
| Shadow | Dark blue tint with reduced alpha |
| Pixelate | Resolution reduction and restoration |
Defining Effects
Effects are defined with createMaterialEffect, which takes a schema for per-sprite data and a TSL node builder:
import { createMaterialEffect } from 'three-flatland'import { tintAdditive, hueShift } from '@three-flatland/nodes'import { vec4 } from 'three/tsl'
const DamageFlash = createMaterialEffect({ name: 'damageFlash', schema: { intensity: 1 } as const, node: ({ inputColor, attrs }) => { const flashed = tintAdditive(inputColor, [1, 1, 1], attrs.intensity) return vec4(flashed.rgb.mul(inputColor.a), inputColor.a) },})
const Powerup = createMaterialEffect({ name: 'powerup', schema: { angle: 0 } as const, node: ({ inputColor, attrs }) => hueShift(inputColor, attrs.angle),})import { createMaterialEffect } from 'three-flatland/react'import { tintAdditive, hueShift } from '@three-flatland/nodes'import { vec4 } from 'three/tsl'
const DamageFlash = createMaterialEffect({ name: 'damageFlash', schema: { intensity: 1 } as const, node: ({ inputColor, attrs }) => { const flashed = tintAdditive(inputColor, [1, 1, 1], attrs.intensity) return vec4(flashed.rgb.mul(inputColor.a), inputColor.a) },})
const Powerup = createMaterialEffect({ name: 'powerup', schema: { angle: 0 } as const, node: ({ inputColor, attrs }) => hueShift(inputColor, attrs.angle),})Applying Effects at Runtime
Instantiate an effect, add it to a sprite, and animate its properties:
const flash = new DamageFlash()sprite.addEffect(flash)
// Animate in render loopflash.intensity = Math.max(0, 1 - elapsed / 0.3)
// Switch effectssprite.removeEffect(flash)const flash = useMemo(() => new DamageFlash(), [])
useEffect(() => { sprite.addEffect(flash) return () => sprite.removeEffect(flash)}, [sprite, flash])
useFrame((_, delta) => { flash.intensity = Math.max(0, flash.intensity - delta / 0.3)})Next Steps
- TSL Nodes Guide - Full node reference and Material Effect documentation
- Tilemap - Tile-based map rendering