Open source rendering engine

1-byte character
terminal animation.

AnimByte is a C++ engine for terminal-based frame rendering using pure ASCII characters. Flat buffer model, delta-only writes, ANSI cursor reposition — no external dependencies.

1 byte / char
100+ fps theoretical ceiling
4 public methods
0 dependencies

Overview

How it works

01

Flat buffer

A single heap-allocated char[] of width × height bytes. Every cell maps to one ASCII character. No structs, no colors, no metadata — just characters.

02

Delta tracking

Set_Char only pushes a cell to the dirty list when its value actually changes. Frame_Clean resets only those cells — untouched cells cost zero work.

03

ANSI cursor reposition

Render_Frame writes \033[H to jump the cursor back to (0,0) then writes the full frame string in one write() syscall — no flicker, no clear.

04

Pure ASCII only

Every character is a single byte from the 7-bit ASCII table (0–127). No UTF-8 multibyte sequences, no Unicode, no wide chars — rendering width is always predictable.

AnimByte.hpp — core loop C++
// main loop pattern
AnimByte engine;
engine.Initialise(80, 24);

while (running) {
    engine.Frame_Clean();          // reset only dirty cells

    // write your frame data
    engine.Set_Char(row, col, ch);   // O(1) per cell

    engine.Render_Frame();           // one write() syscall
    usleep(16000);                   // ~60 fps cap
}

API Reference

Public interface

Method Parameters Description
Initialise
→ int
int width, int height
Allocates the flat buffer and fills it with spaces. Must be called before any other method. Returns 0.
Set_Char
→ int
int row, int col, char ch
Writes one ASCII character at the given 1-indexed position. Returns -1 if out-of-bounds or value unchanged (no dirty push). Otherwise 0.
Render_Frame
→ int
none
Builds the full frame string from the buffer, repositions the cursor with \033[H, and flushes everything in one write() to stdout. Returns -1 if frame is empty.
Frame_Clean
→ int
none
Iterates only the dirty list and sets each cell back to ' '. Cost is O(d) where d is the number of changed cells — not O(width × height).
return values notes
0   // success
-1  // out-of-bounds, no-op (value already set), or empty frame
    // -1 is not a fatal error — callers may safely ignore it

Examples

Usage patterns

sine wave
bouncing ball
matrix rain
progress bar
borders
// sine wave across the terminal
AnimByte engine;
engine.Initialise(80, 24);

double t = 0.0;

while (running) {
    engine.Frame_Clean();

    for (int col = 1; col <= 80; col++) {
        int row = (int)(12 + 10 * sin(col * 0.15 + t));
        engine.Set_Char(row, col, '*');
    }

    engine.Render_Frame();
    t += 0.1;
    usleep(16000);
}
// bouncing ball with simple physics
AnimByte engine;
engine.Initialise(80, 24);

double x=40, y=12, vx=0.8, vy=0.4;

while (running) {
    engine.Frame_Clean();

    x += vx; y += vy;
    if (x <= 1 || x >= 80) vx = -vx;
    if (y <= 1 || y >= 24) vy = -vy;

    engine.Set_Char((int)y, (int)x, 'O');

    engine.Render_Frame();
    usleep(8000);
}
// falling column rain — pure ASCII chars only
AnimByte engine;
engine.Initialise(80, 40);

int heads[80] = {0};
int speeds[80];
for (int i=0; i<80; i++) speeds[i] = rand()%3+1;

while (running) {
    engine.Frame_Clean();

    for (int col=0; col<80; col++) {
        char ch = '!' + rand() % 94; // ASCII 33–126
        engine.Set_Char(heads[col] % 40 + 1, col+1, ch);
        heads[col] += speeds[col];
    }

    engine.Render_Frame();
    usleep(40000);
}
// ASCII progress bar
AnimByte engine;
engine.Initialise(60, 5);

for (int p=0; p<=50; p++) {
    engine.Frame_Clean();

    engine.Set_Char(2, 1, '[');
    engine.Set_Char(2, 52, ']');

    for (int i=0; i<p; i++)
        engine.Set_Char(2, i+2, '=');

    if (p < 50)
        engine.Set_Char(2, p+2, '>');

    engine.Render_Frame();
    usleep(40000);
}
// draw a box border with ASCII line chars
void draw_box(AnimByte& e, int r1, int c1, int r2, int c2) {
    e.Set_Char(r1, c1, '+'); e.Set_Char(r1, c2, '+');
    e.Set_Char(r2, c1, '+'); e.Set_Char(r2, c2, '+');

    for (int c=c1+1; c<c2; c++) {
        e.Set_Char(r1, c, '-');
        e.Set_Char(r2, c, '-');
    }
    for (int r=r1+1; r<r2; r++) {
        e.Set_Char(r, c1, '|');
        e.Set_Char(r, c2, '|');
    }
}

AnimByte engine;
engine.Initialise(80, 24);
draw_box(engine, 2, 2, 22, 78);
engine.Render_Frame();

Implementation ideas

What you can build

01

Terminal games

Snake, Tetris, Pong, Breakout — any grid-based game maps directly to the char buffer. Input via raw terminal mode.

02

System monitor

CPU per-core bars, memory usage, network I/O — rendered live at high refresh using ASCII bar chars.

03

Audio visualizer

FFT bins mapped to column heights. Each frame updates only the bars that changed — delta rendering keeps it fast.

04

Cellular automata

Game of Life, Langton's Ant, rule-based 1D automata — each cell is one char, each generation is one frame.

05

ASCII raymarcher

Map distance field samples to brightness chars: . : ; + * # @. Render 3D scenes entirely in ASCII.

06

Network dashboard

Live packet counts, latency histograms, connection maps — a full TUI dashboard without ncurses.

07

Particle systems

Spawn, move, and expire particles. Each particle is a char. The dirty list ensures only active particles are cleared.

08

Build pipeline UI

CI/CD runner with animated progress bars and stage indicators rendered directly to the terminal output.

09

Oscilloscope / scope

Plot real-time sensor or stdin data as scrolling waveforms — replace graphing tools for embedded debugging.