One tile. Thirty lives. Unlimited layouts.
A zero-dependency C++ tile framework for building animated terminal scenes β where every tile is a procedurally rendered rectangle of motion.
simulaii renders to POSIX terminals (Linux, macOS, BSD, WSL) and relies on ANSI escape codes and 24-bit true-color support to position the cursor and color each cell.
There is no terminal-size detection, no window API, no ncurses, no scene graph library. You create tiles, give each one a rectangle, a color, and a pattern β and the framework handles the rest.
That means:
- The library contains no platform-specific code
- It compiles anywhere a C++11 compiler exists
- The same source can drive a Linux TTY, an Android terminal, or a Windows console with ANSI enabled β as long as your terminal supports 24-bit color and cursor positioning
simulaii handles the tiles. Where the terminal lives is up to you.
- Overview
- Features
- The Tile Model
- The 30 Tile Patterns
- Architecture
- Quick Start
- Composing a Scene
- Use Cases
- Why simulaii
- Comparison
- Design Philosophy
- API Reference
- License
simulaii is a tile-based animation framework for the terminal. Every tile is a rectangle. Every rectangle is alive. You place tiles wherever you want on the grid, give each one a color and a pattern, and advance them together to compose a moving scene.
The name is a union of "simulate" and "ASCII". Simulation, because every tile's contents are generated from sine waves, distance fields, and rotation matrices β not from a lookup table. ASCII, because the output is still just characters on a grid.
A terminal is a grid of characters. Most of the time, that grid is static β text goes in, text comes out. But the grid can also be a canvas, and the canvas can be partitioned. Each partition is a tile. Each tile runs its own simulation. Together, they form a scene.
That's the whole idea.
- A tile is a rectangle with a personality. Position, size, color, pattern β nothing else.
- A scene is a collection of tiles. Lay them out however you want. They don't know about each other.
- A frame is one tick per tile. Call
nextFrame()on each, in any order, at any rate. - A pattern is a tile's mind. Thirty of them ship in the box. A thirty-first is one function away.
simulaii doesn't try to own your terminal. It gives you movable, animatable rectangles and lets you arrange them.
- Positioned Rectangles β Every tile lives at
(row, col)with an explicitwidthandheight - Independent State β Each tile has its own color, pattern, and internal clock
- Unlimited Tiles β Instantiate as many as your terminal can hold
- No Layout Engine β You decide where each tile goes; the framework just draws it
- 24-bit True Color β Full RGB via
#RRGGBBhex strings, per tile - Four Block Characters β
β β β βas the default tile palette - Replaceable Glyphs β Swap the four characters on any tile, at any time
- Cell-by-Cell Drawing β Every frame writes directly to the terminal grid
- 30 Built-in Tile Patterns β Ten wave-based, ten radial/flow, ten 3D/4D wireframes
- Time-Based Motion β Each tile carries its own clock; no frame-counter math required
- Deterministic β The same tick on the same tile always produces the same image
- Stateless Between Frames β Patterns store no accumulation; the math is self-contained
- One Class = One Tile β Construct it, style it, tick it
- Bounds Checked β Invalid constructor arguments throw early, not mid-render
- Zero Setup β No init, no shutdown, no global state
- Style Anytime β Color and glyphs can change between frames
simulaii is built on a single abstraction: the animated tile.
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β A TILE β
β β
β position β (row, col) β
β size β (width, height) β
β style β #RRGGBB + 4 glyphs β
β mind β pattern number (1β30) β
β clock β advanced by nextFrame() β
β β
β βββββββββββββββββββββββββββββββββββββββββ β
β β
β On every tick, the tile: β
β 1. evaluates its pattern for each cell β
β 2. maps the value to one of its 4 glyphs β
β 3. writes that glyph at the correct β
β cursor position in the tile's color β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
A scene is what you get when several tiles share the screen:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β tile A (#FF0066, pattern 6) β tile B (#00FFAA, p 24) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β tile C (#FFFFFF, pattern 30) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β tile D (#FFAA00, pattern 3) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
None of the tiles know the others exist. Composition is your job β the framework just gives you rectangles that move.
Every tile picks one pattern from a menu of thirty. A pattern defines what the tile looks like as time advances. Three families, ten patterns each.
Pure sine-based motion. Diagonals, grids, rings, plasmas, vortices.
| # | Name | Description |
|---|---|---|
| 1 | Diagonal Sine | A calm diagonal wave drifting across the tile |
| 2 | Cross Grid | Horizontal and vertical waves crossing into a shifting grid |
| 3 | Concentric Rings | Rings pulsing outward from the tile's center |
| 4 | Double Slit | Two wave sources interfering, physics-lab style |
| 5 | Rotating Spiral | A spiral arm orbiting the tile's center |
| 6 | Classic Plasma | Four waves blended into liquid motion |
| 7 | Breathing Vortex | A spiral whose tightness expands and contracts |
| 8 | MoirΓ© | Stacked waves producing an optical-illusion pattern |
| 9 | Flow Field | Organic, watery turbulence from stacked trig |
| 10 | Full Hypnotic | Spiral + radial + diagonals + breath, combined |
Feels like: still water, then wind, then something alive underneath.
The same family, but with extra layers β rotation, moving sources, dual harmonics.
| # | Name | Description |
|---|---|---|
| 11 | Flowing Diagonal | Two crossed diagonals for a softer, water-like flow |
| 12 | Rotating Cross | Cross waves that slowly rotate about the center |
| 13 | Moving Ripples | A ripple source that wanders around the tile |
| 14 | Drifting Double Slit | Double-slit interference with sources in motion |
| 15 | Galaxy Spiral | Slow-rotating spiral arms, galactic feel |
| 16 | Plasma Fire | Four-wave plasma tuned for warmer motion |
| 17 | Breathing Vortex II | Vortex with a stronger breathing rhythm |
| 18 | MoirΓ© II | Stacked waves shifting at a different rate |
| 19 | Flow Field II | Water-surface turbulence with different constants |
| 20 | Full Hypnotic II | The peak combo β every trick in one frame |
Feels like: the wave tiles, but with more depth β the same water, deeper.
Rotating wireframes projected into 2D, then drawn as ASCII.
| # | Name | Description |
|---|---|---|
| 21 | Rotating Cube | A classic wireframe cube tumbling on two axes |
| 22 | Rotating Octahedron | Eight triangular faces, wireframe-only |
| 23 | Rotating Torus | A donut of points rotating through 3D |
| 24 | Rotating Sphere | Lat/long grid projected onto a rotating sphere |
| 25 | Rotating Pyramid | A square pyramid spinning on its base |
| 26 | Double Helix | Two intertwined strands, DNA-style |
| 27 | Rotating Cylinder | A stack of rings forming a cylinder |
| 28 | Dodecahedron | Golden-ratio vertices, projected and drawn |
| 29 | MΓΆbius Strip | The classic one-sided surface, rotating |
| 30 | Tesseract | A 4D hypercube projected down to 2D |
Feels like: geometry homework, if geometry homework was hypnotic.
simulaii tile(2, 4, 60, 20, "#00FFAA", 30); // tesseract tile, mint greenβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Scene Layer β
β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β
β β tile A β β tile B β β tile C β β
β β (its own β β (its own β β (its own β β
β β rect, β β rect, β β rect, β β
β β color, β β color, β β color, β β
β β pattern, β β pattern, β β pattern, β β
β β clock) β β clock) β β clock) β β
β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β
β β
β You compose this. The framework doesn't. β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Tile Internals β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β position β’ size β’ color β’ glyphs β’ clock β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Pattern Dispatcher β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β switch(pattern) β drawBoxPatternN() β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Per-Cell Math Layer β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β sin / cos / atan2 / sqrt β value in [0, 1] β β
β β value β index in [0, 3] β block character β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Terminal Layer β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Cursor positioning β’ Foreground color β β
β β Row-by-row write β’ Reset after frame β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
Terminal Display
Each tile draws directly to the terminal, into its own rectangle. There's no framebuffer, no scene graph, no diffing β just characters landing in place, tile by tile, every frame.
Because each tile owns its cursor coordinates, tiles can be laid out side-by-side, stacked, or overlapped. The framework doesn't enforce a layout β it just provides the primitive.
- C++11 or higher compiler
- POSIX terminal β Linux, macOS, BSD, WSL
- 24-bit true-color support β Most modern terminals handle this
- ANSI escape code support β For cursor positioning
simulaii is a single C++ class. Drop it into your project.
# Clone or copy simulaii.cpp into your project
# Compile with optimizations
g++ -O3 -std=c++11 your_scene.cpp -o your_scene
# Run
./your_scene#include "simulaii.cpp"
#include <thread>
#include <chrono>
int main() {
// row col width height color pattern
simulaii tile( 2, 4, 60, 20, "#00FFAA", 6);
while (true) {
tile.nextFrame();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
return 0;
}Six lines. One tile. One loop.
A scene is just more than one tile. Each tile is independent β different color, different pattern, different position β but they share the same loop.
#include "simulaii.cpp"
#include <thread>
#include <chrono>
int main() {
// row col w h color pattern
simulaii header( 0, 0, 80, 3, "#FF0066", 6); // plasma header
simulaii side( 3, 0, 20, 20, "#00FFAA", 24); // rotating sphere
simulaii main( 3, 20, 60, 20, "#FFFFFF", 30); // tesseract
simulaii footer(23, 0, 80, 2, "#FFAA00", 3); // pulsing rings
while (true) {
header.nextFrame();
side.nextFrame();
main.nextFrame();
footer.nextFrame();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
return 0;
}Four tiles. One screen. A live scene.
simulaii tile(2, 4, 60, 20, "#FF0066", 10);
tile.nextFrame();
tile.setColor("#00FFAA"); // new color, same pattern
std::string newGlyphs[4] = {"+","-","=","#"};
tile.changePrimitiveCharacters(newGlyphs); // new glyph set
tile.nextFrame();Tiles are stylable at runtime. Color and glyphs are not fixed.
const int TILE_W = 20, TILE_H = 10;
int p = 1;
for (int r = 0; r < 3; ++r)
for (int c = 0; c < 4; ++c)
tiles.emplace_back(r * TILE_H, c * TILE_W, TILE_W, TILE_H,
"#FFFFFF", p++);That's a 12-tile grid, one pattern per tile. Compose whatever you want.
A grid of tiles, each showing a different animated indicator. Plasma for "running," rings for "waiting," a sphere for "synced." The tile model maps directly onto a dashboard layout.
A header tile with a slow plasma wave. A footer tile pulsing in your brand color. A corner tile showing a rotating cube while a build runs.
Multiple tiles composing a layout while your installer copies files. Header tesseract, side dodecahedron, main progress box, footer ripples.
You already use a TUI library for input and layout β simulaii becomes the content of every panel. Tiles fill the rectangles your layout engine creates.
A tile-based overlay in OBS. Compose your scene, style each tile to match your brand, run it. The tiles don't know they're on stream.
A full-screen grid of thirty tiles, one per pattern, all advancing together. A showcase scene.
Every pattern is a pure function of (i, j, t) inside a fixed rectangle. Read the source, change a constant, see what happens. A tile is the smallest unit of graphics you can play with.
Most terminal animation libraries either go tiny (a spinner) or enormous (a full TUI framework with a scene graph, layout engine, and event loop). simulaii sits in the middle.
It gives you one primitive β the animated tile β and lets you build the rest. Layout is your job. Composition is your job. Timing across tiles is your job. But everything inside a tile is done for you, thirty times over.
It doesn't want to own your terminal. It wants to hand you rectangles that move, and then get out of the way.
If you're building anything in a terminal that has more than one region β a dashboard, a status bar, a live footer, a boot screen β you already have tiles. simulaii is what fills them.
| simulaii | Manual ASCII loops | TUI Frameworks | Graphics Libraries | |
|---|---|---|---|---|
| Dependencies | None | None | Several | Many |
| Setup | One class per tile | Custom per region | Framework to learn | Build system, drivers |
| Unit of Work | Animated tile | Custom | Widget | Framebuffer |
| Tiles in a Scene | Unlimited | Manual | Widget instances | N/A |
| Tile Patterns Included | 30 | 0 | ~0 | β |
| True Color | Yes | Manual | Varies | N/A |
| Runs in Terminal | Yes | Yes | Yes | No |
| Learning Curve | Minutes | Hours | Hours to days | Days to weeks |
| Scope | Animated tiles | Anything | Full-screen UI | Full-screen pixels |
simulaii doesn't replace your TUI library. It becomes the tile primitive inside it β one class per region, thirty ways to make each region move.
simulaii is deliberately narrow.
- It animates one rectangle.
- It uses four characters.
- It exposes one method for advancing time.
- It ships with thirty patterns, but adding a thirty-first is a single function.
Every tile is a self-contained unit. It doesn't know about other tiles. It doesn't know the terminal size. It doesn't know the frame rate. It just knows: "I am a rectangle at (row, col), this big, this color, running this pattern, and I advance one tick when told."
That minimalism is the point. A tile is the smallest animated thing that can exist on a terminal. Compose tiles; get scenes.
Every pattern is a pure function of position and time. No frame buffers, no noise textures, no asset files. The math is the animation.
You're not meant to leave it alone. Change the constants. Rewrite a pattern. Replace the block characters with emoji. Lay tiles out however you want. The source is short enough to read in one sitting, and the tile primitive is simple enough to fork in an afternoon.
It's a framework for people who want to see something move β and who want to compose the motion themselves.
simulaii(int row, int col, int width, int height,
std::string color, int patternNumber = 1);Creates a tile and immediately renders the first frame of the chosen pattern.
row,colβ 0-indexed top-left corner of the tilewidth,heightβ size of the tile in cellscolorβ hex color string,"#RRGGBB"patternNumberβ1to30
Throws std::invalid_argument on bad bounds or an out-of-range pattern.
int nextFrame();Advances this tile's internal clock by one tick and redraws the entire tile. Other tiles are unaffected β you decide the tick order in your scene loop.
Returns 0 on success.
int setColor(std::string hexColor);Changes the tile's foreground color. Takes effect on the next frame.
Throws std::invalid_argument if the string is not #RRGGBB.
int removeColor();Resets the tile's color to white (#FFFFFF).
int changePrimitiveCharacters(std::string newChars[4]);Replaces the four glyphs used to render the tile. Only the first character of each string is used.
Default glyphs: β β β β
Licensed under the apache 2.0 License. See LICENSE for details.
One tile. Thirty lives. Unlimited layouts.
"Every tile is a rectangle. Every rectangle is alive. The scene is yours to compose."



