Skip to content

stussysenik/vfx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vfx

Demo

Terminal-native 2D graphics engine — 18 GPU-quality effects at 60fps using braille characters + true color

Go License: MIT Effects Palettes

What happens when you try to build a real graphics engine inside a terminal?

No GPU. No framebuffer. No pixel-level addressing. Just Unicode characters and ANSI escape codes. The constraint sounds impossible — but Unicode braille characters (U+2800–U+28FF) encode 8 dots per cell in a 2x4 grid, giving you 4x the resolution of half-block rendering. Pair that with 24-bit true color and suddenly a 200-column terminal becomes a 400x200 pixel canvas running at 60fps.

This started as a weekend experiment: could I get SPH fluid dynamics running in a terminal? One effect became two, two became six, and now it's an 18-effect demoscene engine with Perlin noise fields, fractal zooms, Reynolds flocking, and a spinning 3D torus — all rendered through braille dot patterns.

Built with Bubbletea v2 and pure Go. No CGO. No shaders. Just fmt.Fprintf and math.

Install

go install github.com/stussysenik/vfx@latest

Or build from source:

git clone https://github.com/stussysenik/vfx.git
cd vfx
go build -o vfx .
./vfx

Note: You need ./vfx (not just vfx) because the current directory isn't in your PATH. Using go install . instead will place the binary in $GOPATH/bin (usually ~/go/bin), which is typically in your PATH.

Built With

Language Go 1.25 — pure Go, zero CGO
TUI framework Bubbletea v2 — Elm-architecture terminal UI
CLI Cobra — command routing (play, list, demo, screenshot)
Styling Lip Gloss v2 — terminal layout and color
Rendering Unicode braille (U+2800–U+28FF) — 8 sub-pixels per character cell
Color 24-bit ANSI true color (\e[38;2;R;G;Bm) — 16.7M colors
Pixel buffer []float32 RGBA — same layout as a GPU framebuffer
Noise Custom Perlin noise (2D/3D) — no external dependency
Screenshots image/png stdlib — headless rendering to PNG

No GPU. No shaders. No external C libraries. Every pixel is computed in Go and drawn through Unicode characters.

Usage

vfx                    # Interactive browser — pick effects from a list
vfx play fire          # Play a specific effect fullscreen
vfx list               # List all 18 effects
vfx demo               # Screensaver mode — auto-cycles every 10 seconds
vfx --seed 42 play plasma  # Reproducible with a fixed seed

Effects Gallery

Basic


plasma
Sinusoidal color plasma

lissajous
Parametric oscilloscope curves

starfield
3D warp speed projection

waves
Sine wave interference

matrix
Digital rain cascade

aurora
Northern lights curtains

Intermediate


fire
DOOM PSX fire propagation

particles
Gravity particles with trails

metaballs
Organic blobby isosurface

terrain
3D wireframe Perlin terrain

flowfield
Noise-driven particle flow

life
Conway's Game of Life

Advanced


fireworks
Particle burst explosions

mandelbrot
Animated Mandelbrot zoom

julia
Animated Julia set fractal

donut
Spinning 3D torus

boids
Reynolds flocking simulation

fluid
SPH fluid dynamics

Controls

Global (all effects)

Key Action
h Toggle HUD overlay (effect name, FPS, controls)
space Pause / resume
n Next effect
N Previous effect
r Randomize seed
p Cycle color palette (where supported)
q / esc Quit to browser (or exit)

Browser Mode

Key Action
↑/↓ / j/k Navigate effect list
enter Launch selected effect fullscreen
/ Filter effects
q Quit

Palettes

13 built-in color palettes, each defined as gradient stops with linear interpolation:

fire · neon · ocean · mono · dracula · tokyo-night · catppuccin · gruvbox · nord · solarized · retrowave · sunset · matrix

Press p during any effect to cycle through them.

How It Works

Braille Rendering

Each terminal character cell maps to a 2x4 braille dot grid. The Unicode braille block (U+2800–U+28FF) encodes which of the 8 dots are "lit" as a bitmask:

Cell layout:      Bit positions:
 [·][·]            [0][3]
 [·][·]            [1][4]
 [·][·]            [2][5]
 [·][·]            [6][7]

An 80x24 terminal becomes a 160x96 pixel canvas. A fullscreen 200x50 terminal becomes 400x200 — enough for smooth particle physics and fluid dynamics.

Each braille character gets a single 24-bit foreground color via ANSI escape codes (\e[38;2;R;G;Bm), computed by averaging the lit pixels in that cell.

Rendering Pipeline

Effect.Update(dt)          → advance simulation state
Effect.Draw(canvas)        → write float32 RGBA pixels to buffer
Canvas.Render()            → convert pixels to braille chars + ANSI color
Bubbletea View()           → display frame in alternate screen

The canvas uses a flat []float32 buffer (RGBA per pixel) — the same layout as a GPU framebuffer. Effects write directly to it using SetPixel, AddPixel (additive blending), or drawing primitives (Line, Circle, FilledCircle, Rect).

Architecture

cmd/                    CLI commands (cobra)
├── root.go             Browser mode (default)
├── play.go             vfx play <effect>
├── list.go             vfx list
├── demo.go             vfx demo
└── screenshot.go       vfx screenshot — headless PNG export

internal/
├── canvas/             Pixel buffer + terminal rendering
│   ├── canvas.go       float32 RGBA buffer → braille/halfblock + ANSI + PNG export
│   ├── color.go        HSL↔RGB, LerpColor, RGBA type
│   └── blend.go        Normal, additive, multiply, screen blending
├── engine/             Core abstractions
│   ├── engine.go       Effect interface, Control, Range
│   ├── particle.go     Reusable ParticleSystem + Emitter
│   ├── noise.go        Permutation-based Perlin noise (2D/3D)
│   └── physics.go      Gravity, force calculations
├── effects/            18 visual effects (one file each)
│   ├── registry.go     Effect registration
│   ├── helpers.go      Shared utilities (clampf, cyclePalette)
│   ├── fire.go         DOOM fire propagation
│   ├── plasma.go       Sinusoidal plasma
│   └── ...             (15 more)
├── palette/            Color gradient system
│   ├── palette.go      Gradient stops + linear interpolation
│   └── builtin.go      13 curated palettes
└── tui/                Bubbletea v2 terminal UI
    ├── app.go          Root model (browser↔player transitions)
    ├── browser.go      Effect list with live preview
    ├── player.go       Fullscreen 60fps renderer
    ├── overlay.go      HUD (FPS, controls, effect name)
    └── theme.go        Terminal color theme

Every effect implements a single interface:

type Effect interface {
    Name() string
    Description() string
    Category() string
    Init(width, height int, seed int64)
    Update(dt float64)
    Draw(c *canvas.Canvas)
    Controls() []Control
    HandleInput(key string)
}

To add a new effect: create a file in internal/effects/, implement the interface, register it in registry.go. That's it — the browser, player, demo mode, and palette cycling all work automatically.

Generating Screenshots

The screenshot command renders effects headlessly to PNG — useful for documentation, sharing, or testing:

vfx screenshot plasma -o plasma.png           # Single effect
vfx screenshot --all -o docs/                 # All 18 effects into a directory
vfx screenshot julia --width 640 --height 360 # Custom resolution
vfx screenshot --all --seed 42 -o docs/       # Reproducible with fixed seed

Flags: --width (default 320), --height (default 180), --frames (default 120 = 2 seconds at 60fps).

Inspiration

This project builds on ideas from many sources:

  • donut.c by Andy Sloane — the rotating torus math
  • DOOM PSX fire by Fabien Sanglard — upward heat propagation
  • Boids by Craig Reynolds (1987) — separation, alignment, cohesion flocking rules
  • Improved Perlin Noise by Ken Perlin (2002) — the smoothstep and gradient functions
  • SPH fluid simulation by Muller et al. (2003) — pressure and viscosity kernels
  • Classic demoscene plasma — sinusoidal interference patterns as a tradition
  • drawille — braille characters as pixel grids

Recording GIFs

VHS tape files are included in docs/ for reproducible recordings:

brew install vhs
vhs docs/fire.tape      # → docs/fire.gif
vhs docs/plasma.tape    # → docs/plasma.gif

Requirements

  • Go 1.25+
  • A terminal with true color support (iTerm2, WezTerm, Ghostty, Kitty, Alacritty)
  • Terminal.app works but with limited color accuracy

License

MIT


See ARCHITECTURE.md for a deep dive into the technical decisions behind vfx.

About

Terminal-native 2D graphics engine — 15 GPU-quality animations at 60fps using braille characters + true color

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages