# @solidrt/2d examples

Single-concept sprite-layer patterns. Each file is a complete, runnable app
(`bun run sol run <file>` from the repo root) demonstrating exactly one thing -
copy one and adapt it.

- `sprites.tsx` - the layer at its natural scale: 500 sprites bouncing at
  frame rate, moved imperatively with `setSprite` from `onFrame` while the
  tree holds one texture leaf. Atlas from PNG bytes (`createAtlas`) sliced
  2x2 with `grid()`.
- `atlases.tsx` - several atlases in ONE layer: the logo sheet and a
  tileset built in code, declared together as the layer's `atlases`, its
  sprites drawing from either and interleaved by `orderBy: "y"` across
  both sheets in the one draw. The tileset is uploaded with `mipmap` for
  the far zoom, so it goes through `extrudeGrid` first (a 4-texel gutter
  of replicated edge pixels per cell, the returned options slicing the
  repacked sheet): zoom out and the cells' rims stay crisp instead of
  smearing the neighbour in. Wheel/pinch zooms, drag pans. Debug
  commands `zoom` and `atlases` drive it headless.
- `tiles.tsx` - the baked tile layer as a streaming world: a ring road
  10^7 px from the origin seeded by ONE `setTiles` rect write over a
  Uint32Array of frame indices with flip and transpose bits (the layer's
  `frames` table; -1 where the world is empty, and those chunks never
  hold records), flown over by a ship-style camera (fixed screen pivot,
  the world panning and ROTATING under it via the `<TileLayer>` camera
  prop - transform writes, never a re-bake) while the residency gate
  brings the chunks near the view in and takes the far ones out (their
  targets through the resource pool), a north-up minimap as a second
  view of the same layer (`<TileLayer layer>`, minifying the bake through
  the chunks' mip chain), a
  `maxTexels` budget of 40 chunks that the gate evicts past (the minimap's
  far chunks stall, holes rather than blur), and a timer editing tiles to
  show that a `setTile` batch re-bakes only the resident chunks it
  touches, paced under the bake budget (`setBakeBudget`, nearest the
  ship first). Read `/gpu?label=tile-world` (the `groups` footprint) and
  `/stats` (regionsResident, residentWeight, app.poolHits) while it
  flies. Debug commands `state` and `cell`.
- `anim.tsx` - frame animation with `createAnimation`: three sprites share
  one looping clip (one clock, stepped by wall time independent of display
  rate) and a one-shot clip holds its last frame and fires `onEnd`.
  Self-asserting - watch the logs for ANIM-OK.
- `camera.tsx` - the 2d camera (`createCamera2d`) over a world larger than
  the window, driving the layer's one view from an input map the app
  binds (`camera2dBindings` over the view's pointer feed, bridged from
  the view's root with `feedPointer`, and any pad): drag empty space to
  pan with inertia, wheel/pinch to zoom about the pointer, tap empty
  space to glide there (the view's `onTap` with `e.sprite` null), tap a
  sprite to select it, drag a sprite to move it (the sprite stops its
  down, so the camera never pans under it), F to follow a roaming sprite
  through a dead zone, R to spin the view, Space to fit the world. The
  VIEW's `handlers` spread onto its leaf; `update(dt)` from `onFrame` is
  the only per-frame call. Debug commands `camera`, `mode`, `selected`
  and `first` drive it headless.
- `pick.tsx` - the event model through the component face: exact
  rotated-rect hit testing topmost-first, `onTap` (the dispatch's own
  click, no slop bookkeeping), a claimed press dragging a sprite under a
  `<Camera2d>` that pans empty space and zooms on wheel, the layer's own
  `onTap` seeing misses with `e.sprite` null (and `tapCount` for double
  taps), and removal through a signal so `<For>` unmounts the `<Sprite>`
  and the layer recycles the slot. A corner `<View2d>` inset shows the
  same sprites again under a `<Camera2d>` of its own (drag or wheel on
  the inset moves only the inset's camera; a sprite tapped or dragged
  there responds as in the main view). The `state` debug command reads
  both cameras and the sprite positions back.
- `views.tsx` - layer views (`layer.createView`, the only way a layer
  shows): the world rendered twice from ONE set of sprites - the
  window-filling main view under a `createCamera2d` and a corner minimap
  showing the whole world at the fit zoom, a stroked rect outlining the
  main view rect through `map.project`. A tap on the minimap glides the main camera there (the
  view's root listener, `e.x`/`e.y` in world pixels), and a tap on a
  sprite in the minimap selects it as in the main view (the same sprite
  handlers, the walk ending at the view). Debug commands `state`,
  `camera`, `selected` and `first` drive it headless.
- `markers.tsx` - screen-size markers and an additive glow layer: two
  layers under ONE `createCamera2d` (both views as targets), the world
  layer carrying a constant-size pin per sprite (`minScreenPx` =
  `maxScreenPx`), a label bar that never grows past 24 px (`maxScreenPx`)
  and a selection ring that never shrinks under 48 px (`minScreenPx`,
  gliding to the tapped sprite through a position transition), the glow
  layer the same atlas on `unlit({ blend: "add" })` with haloes that add up to white
  where they overlap. Picking follows the drawn size. Debug commands
  `zoom`, `select` and `pick` drive it headless.
- `split-screen.tsx` - one `<SpriteLayer output={false}>` (sprites, no
  leaf of its own) shown through two `<View2d>` panes filling the window
  side by side, each with a `<Camera2d>` of its own following one of two
  roaming sprites through a dead zone, so the two poses differ every
  frame; wheel zooms a pane alone while its follow keeps tracking, a tap
  on a sprite in either pane tints it in both (one sprite, two views).
  Each pane's map binds `gamepad.next()`: the first pad to press any
  button drives pane 0, the next pane 1. The `cameras` debug command
  reads both poses back.
- `text.tsx` - world-space text: a sprite font (`createSpriteFont`, the
  runtime's glyph engine behind `flux:font`) declared as one more atlas of
  the layer, each sprite carrying a name below it as a text run
  (`addText`: a group of glyph sprites in the one draw, anchored on its
  middle, an `outline` for the msdf kind), a counter re-set every half
  second (`setText` reuses the run's sprite pool) and a title at twice
  the face's size through the component face: a `<Text2d>` under a
  `<SpriteLayer layer={layer} output={false}>` adopting the same
  imperative layer. Wheel/pinch zooms, drag pans; the labels ride the
  camera with no JS per label. Debug commands `zoom` and `state` drive
  it headless.
