ASCII GIF viewer
Decodes a .gif on disk via ext-gd, downsamples each frame to a configurable cell grid, and renders the animation as ANSI-coloured Unicode block-glyphs. Two presets: solid 24-bit blocks or a luminance-ramp ASCII rendering.
composer require sugarcraft/candy-flip
candy-flip my-animation.gif # solid blocks (default)
candy-flip my-animation.gif density # luminance ramp
// Programmatic:
use SugarCraft\Core\Program;
use SugarCraft\Core\ProgramOptions;
use SugarCraft\Flip\Decoder;
use SugarCraft\Flip\Player;
$frames = Decoder::decode('cat.gif', cellsW: 80, cellsH: 30);
(new Program(new Player($frames),
new ProgramOptions(useAltScreen: true)))->run();
solid โ full-cell โ in 24-bit truecolor. density โ luminance ramp ` .:-=+*#%@`.Space pauses; โ / โ step through frames manually.d swaps solid โ density without losing your position.Renderer::withAdaptiveSize() queries the TTY via SizeIoctl (ioctl TIOCGWINSZ) so the output never overflows the viewport. Call withConstraints() with explicit limits to test rendering at fixed dimensions.Frame object identity โ repeated playback skips re-rendering, and entries are dropped automatically when the Frame is garbage-collected.Cmd::tick($interval, โฆ) schedules frame advance โ no busy-waiting in the main fiber.| Class | Method | Description |
|---|---|---|
| Decoder | decode(path, cellsW, cellsH) | Load and resize GIF |
| FrameCache | new() | WeakMap-backed render cache |
| FrameCache | get(frame) / set(frame, rendered) / has(frame) / delete(frame) / clear() | Cache read/write/reset |
| Player | new(frames) | Create player |
| Program | new(player, options) | Create program |
| Program | run() | Start the event loop |
| ProgramOptions | new(useAltScreen) | Configure terminal mode |
| Renderer | withAdaptiveSize() | Query TTY size via SizeIoctl, clamp output |
| Renderer | withConstraints(rows, cols) | Set explicit render limits (testing) |
| Renderer | renderFrame(frame, preset) | Render a frame to ANSI string |
VHS-recorded GIFs of every example shipped with the app. Regenerated automatically on every push that touches the source.