Skip to content
Conduit

skill

three-flatland

The composable 2D library for Three.js

thejustinwalsh/three-flatlandcommunitylistedunreviewed

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/thejustinwalsh/three-flatland itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 17, 2026

three-flatland

2D sprites, tilemaps, and TSL-composable effects for Three.js, built for WebGPU.

[!IMPORTANT] Alpha — three-flatland is in active development. The library targets GPU-driven 2D rendering on WebGPU; the API will evolve as the underlying systems settle. Open an issue or comment on GitHub to shape what gets built.

Features

  • 2D primitives on the Three.js scene graph. Sprite2D, AnimatedSprite2D, SpriteGroup, and TileMap2D as plain Object3D subclasses. Transform hierarchy and render order are independent (sortLayer + zIndex).
  • TSL-native effect composition. Effects are TSL node graphs that ride on a shared material. Sprites in a batch stay batched as effects come and go.
  • Effect-aware sprite batching. Compatible sprites stay batched as effects change, with per-sprite values packed into shared GPU buffers.
  • Spritesheet animation with frame-precise timing. AnimationController handles play/pause/onComplete; declare named animations against a sheet.
  • Tilemap loaders for Tiled and LDtk. Animated tiles supported.
  • Pixel-perfect by default. FlatlandConfig coordinates texture loading, integer camera scale, and projected-pivot snapping; override the whole pipeline or one subsystem.
  • Render to texture for 2D-on-3D. The Flatland class composes a 2D scene with a pixel-perfect orthographic camera and optional RenderTarget; sample the result on any 3D material (mesh.material.map = flatland.texture).
  • React Three Fiber integration via three-flatland/react. Re-exports the core surface plus JSX type augmentation; attachEffect covers the add/remove lifecycle.
  • Tree-shakeable subpath exports. three-flatland/sprites, /animation, /loaders, /cameras, /config, /pipeline, /tilemap, /materials. Import only what you use.

Installation

# Core library (Three.js)
npm install three-flatland three

# For React Three Fiber
npm install three-flatland @react-three/fiber@10.0.0-alpha.3 react@~19.2.0 react-dom@~19.2.0

# TSL shader nodes
npm install @three-flatland/nodes

For a new project, run pnpm create three-flatland and choose the Three.js or React template. See installation for the available options.

Quick Start

Three.js

import { WebGPURenderer } from 'three/webgpu'
import { Scene } from 'three'
import { PixelPerfectCamera, Sprite2D, SpriteGroup, TextureLoader } from 'three-flatland'

const scene = new Scene()
const camera = new PixelPerfectCamera({ viewSize: 600 })

const renderer = new WebGPURenderer()
renderer.setSize(800, 600)
camera.setDrawingBufferSize(renderer.domElement.width, renderer.domElement.height)
renderer.setViewport(camera.getLogicalViewport(renderer.getPixelRatio()))
document.body.appendChild(renderer.domElement)
await renderer.init()

const texture = await TextureLoader.load('/sprite.png')

// SpriteGroup handles automatic batching
const group = new SpriteGroup()
scene.add(group)

const sprite = new Sprite2D({ texture, anchor: [0.5, 0.5] })
group.add(sprite)

function animate() {
  requestAnimationFrame(animate)
  renderer.render(scene, camera)
}
animate()

React Three Fiber

import { Canvas, extend, useLoader } from '@react-three/fiber/webgpu'
import { Suspense } from 'react'
import { Sprite2D, SpriteGroup, TextureLoader, usePixelPerfectCamera } from 'three-flatland/react'

extend({ Sprite2D, SpriteGroup })

function Camera() {
  usePixelPerfectCamera({ viewSize: 600 })
  return null
}

function Sprite() {
  const texture = useLoader(TextureLoader, '/sprite.png')
  return (
    <spriteGroup>
      <sprite2D texture={texture} anchor={[0.5, 0.5]} />
    </spriteGroup>
  )
}

export default function App() {
  return (
    <Canvas orthographic>
      <Camera />
      <Suspense>
        <Sprite />
      </Suspense>
    </Canvas>
  )
}

Core Concepts

Sort layers and z-ordering

Unlike traditional 3D engines, three-flatland separates transform hierarchy from render order:

import { SortLayers, Sprite2D } from 'three-flatland'

// Scene graph controls position inheritance
const player = new THREE.Group()
const shadow = new Sprite2D({ texture: shadowTex })
const body = new Sprite2D({ texture: bodyTex })
player.add(shadow, body)

// Render order is explicit and independent
shadow.sortLayer = SortLayers.SHADOWS // Renders first
body.sortLayer = SortLayers.ENTITIES // Renders on top

// Shadow moves with player but always renders below
player.position.x += 10

Animation

import { AnimatedSprite2D, SpriteSheetLoader } from 'three-flatland'

const sheet = await SpriteSheetLoader.load('/sprites/player.json')

const player = new AnimatedSprite2D({
  spriteSheet: sheet,
  animationSet: {
    animations: {
      idle: { frames: ['idle_0', 'idle_1', 'idle_2'], fps: 8 },
      run: { frames: ['run_0', 'run_1', 'run_2', 'run_3'], fps: 12 },
      attack: { frames: ['attack_0', 'attack_1'], fps: 15, loop: false },
    },
  },
  animation: 'idle',
})

player.update(deltaMs)
player.play('run')
player.play('attack', { onComplete: () => player.play('idle') })

Composable TSL Effects

import { createMaterialEffect } from 'three-flatland'
import { tintAdditive } 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 flash = new DamageFlash()
sprite.addEffect(flash)
flash.intensity = 0.8 // Animate per frame

Packages

PackageDescription
three-flatlandDefault entry. Sprites, animation, tilemaps, materials, lights, events, everyday loaders.
three-flatland/reactReact Three Fiber subpath. Re-exports core plus JSX type augmentation.
create-three-flatlandProject scaffolder. npm create three-flatland@latest.
@three-flatland/nodesA specific 2D shader effect (retro/CRT, blur, distortion, color, upscale) without hand-writing TSL.
@three-flatland/presetsLit sprites working immediately. Thin — DefaultLightEffect and NormalMapProvider.
@three-flatland/normalsDynamic lighting on flat 2D art without hand-authoring normal maps.
@three-flatland/atlasLoose sprite PNGs into one draw-call-friendly atlas, optionally polygon-trimmed for overdraw.
@three-flatland/alphamapPixel-perfect pointer hit testing on transparent sprites instead of bounding-box hits.
@three-flatland/imagePNG/WebP/AVIF/KTX2 encode and decode, plus Ktx2Loader.
@three-flatland/bakeOffline-bake framework and the flatland-bake binary.
@three-flatland/slugText that stays sharp at any zoom or perspective; thousands of glyphs in one draw call.
@three-flatland/skiaImmediate-mode 2D canvas in the scene: arbitrary paths, boolean ops, filters, gradients, images.
@three-flatland/devtoolsLive inspection of scene, material, and sprite state.
@three-flatland/skillsAgent skills for TSL and the three-flatland ecosystem.

Requirements

  • three ^0.185.1 (TSL/WebGPU support)
  • React ~19.2.0 (for three-flatland/react, capped below 19.3 by R3F alpha.3)
  • @react-three/fiber 10.0.0-alpha.3 (for React, WebGPU support)

Documentation

Full docs, interactive examples, and API reference at tjw.dev/three-flatland.

When not to reach for three-flatland

  • 3D scenes. Use Three.js directly. three-flatland adds nothing for non-2D work and the batching system assumes orthographic-style 2D composition.
  • WebGL-1-only targets. WebGPURenderer can use Three.js's WebGL 2 node backend when WebGPU is unavailable; legacy WebGL 1 is out of scope.
  • DOM-overlay UI. For HTML UI layered over a canvas, use the DOM. three-flatland is a renderer, not a UI toolkit.

Roadmap

  • Core sprite system (Sprite2D, materials, loaders)
  • Animation system (AnimatedSprite2D, AnimationController)
  • 2D render pipeline with effect-aware sprite batching
  • TSL effect nodes (composable shader nodes)
  • Tilemap support (Tiled, LDtk)
  • React Three Fiber integration
  • Skia GPU text rendering via WASM
  • Render targets for 2D-on-3D (via Flatland class)
  • Slug text rendering (@three-flatland/slug)
  • 2D lighting & shadows (@three-flatland/presets)
  • VS Code tools (three-flatland.fl-tools)
  • Effect presets (@three-flatland/presets)
  • Particles

License

MIT

↑↓ navigate openesc close