Skip to content

Navigation Menu

Sign in
Sign up

Latest commit

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

ascii-waves

An animated ASCII wave simulator for the terminal, in 24-bit color. Single file, pure Python standard library, no dependencies.

demo

python3 waves.py

Press ? for the key bindings, q to quit.

Scenes

Cycle through them live with m.

ocean ripples
ocean — open-water swell ripples — droplet impacts
interference spiral
interference — point sources spiral — a rotating rotor wave
  • ocean sums a spectrum of directional sine waves under a deep-water dispersion relation, so long swell outruns short waves. Crests are sharpened, troughs flattened, and a short-wavelength component adds chop.
  • ripples drops stones on calm water. Each impact sends out a wave packet — a gaussian envelope riding an expanding ring — that decays with age.
  • interference holds several point sources at fixed positions and lets their wavefronts beat against each other.
  • spiral winds the phase around the center to make rotating arms.

Keys

Key Action
q / Esc quit
space pause
m / M next / previous scene
c / C next / previous palette
a next character ramp
r randomize the current scene
d disturb — drop a stone, gust the wind, move sources
+ / - speed
[ / ] amplitude
h toggle the status bar
? help

Options

-m, --mode ocean | ripples | interference | spiral
-p, --palette ocean | sunset | neon | magma | ice | matrix | ember | mono
-a, --ramp classic | ascii | blocks | shade | dots | sea
 --color truecolor | 256 | none (auto-detected; NO_COLOR is honored)
 --fps target frame rate (default 30)
-s, --speed time multiplier
 --amplitude wave height multiplier
 --seed reproducible wave sets
 --width override the terminal width
 --height override the terminal height
 --frames N render N frames and exit, on a fixed timestep
 --no-hud hide the status bar
python3 waves.py -m ripples -p ice -a blocks
python3 waves.py -m spiral -p magma --speed 0.4 --no-hud

--frames renders a fixed number of frames on a fixed timestep and exits, so batch output is byte-identical regardless of how fast the machine is. Combined with --seed it makes runs reproducible, which is how the images above were generated.

How it works

The height field is a sum of sinusoids evaluated per cell, quantized into 48 levels. Each level indexes a precomputed table of (ANSI escape, glyph) pairs, so color and character both fall out of one lookup.

Two things keep it fast enough to run in pure Python at full-screen sizes:

  • Integer phase accumulation. Sines come from a 4096-entry lookup table indexed by an integer phase. Because that index is linear in the phase, phases add as plain integers, and everything static about a wave — k·x per column, k·r per cell for a point source — is precomputed once at resize time. Each cell then costs an integer add, a mask, a table lookup and a multiply.
  • Run-length color. An escape sequence is emitted only when the level changes from the previous cell, which cuts about a fifth of the output bytes.

A ×ばつ55 frame takes roughly 8 ms to compute and render, so the 30 fps default has plenty of headroom.

Two details worth knowing if you read the source:

  • Terminal cells are about twice as tall as they are wide, so the vertical axis is scaled by 2 to keep wavefronts circular rather than elliptical.
  • time.sleep() overshoots by several milliseconds on some platforms (~8 ms on macOS). The frame pacer tracks that error and asks for correspondingly less on the next frame; without it a 30 fps target lands at about 24 fps.

Requirements

Python 3.8 or newer, and a terminal that understands ANSI escapes.

Color depth is detected from COLORTERM and TERM, and NO_COLOR is honored; --color overrides the guess. Over a slow link, prefer --color 256 — a full-screen truecolor frame is around 50 KB, while 256-color escapes are much shorter.

Interactive keys need a POSIX terminal (macOS, Linux, BSD, WSL). On native Windows the animation renders, but key handling is unavailable and Ctrl-C is the way out.

Regenerating the images

tools/capture.py rasterizes frames by rendering each frame's text once as a white-on-black mask and multiplying it by a per-cell color image, so the images use real terminal glyphs rather than an approximation. It needs ImageMagick, and ffmpeg for the GIF. See the comments at the top of that file.

License

MIT — see LICENSE.

About

Animated ASCII wave simulator for the terminal, in 24-bit color. Pure Python, no dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /