A terminal-based 3D ASCII torus-knot animation written in C.
This program renders a rotating (p, q)-torus knot directly inside the terminal using mathematical projection, trigonometry, depth buffering, and ASCII shading techniques.
The animation continuously rotates in real-time and simulates 3D lighting entirely using text characters.
The default knot is a trefoil knot (p=2, q=3), the simplest non-trivial knot in mathematics.
This project focuses on learning:
- terminal rendering
- parametric 3D curves
- tube geometry around a curve
- Frenet-style orthonormal frames
- 3D projection math
- trigonometry in graphics
- depth buffering
- animation loops
- low-level rendering concepts in C
- Real-time rotating ASCII torus knot
- 3D perspective projection
- Depth buffer (Z-buffer) implementation
- ASCII lighting and shading
- Pure terminal rendering
- Written entirely in C
- Configurable (p, q) winding numbers
- Uses trigonometric functions for rotation
The program mathematically walks along the center curve of a torus knot and wraps a thin tube around that curve. Every point on the tube is rotated, projected to the terminal, and shaded by its surface normal.
For every frame:
- Clears previous frame buffer
- Walks the knot curve at thousands of points along parameter
t - For each
t, builds an orthonormal frame perpendicular to the tangent - Sweeps a circle around the tube cross-section
- Applies rotation transformations
- Projects 3D points into 2D screen space
- Computes brightness from the surface normal
- Uses ASCII characters as pixels
- Prints the updated frame to the terminal
The animation repeats continuously to create smooth rotation.
Two buffers are created:
char buf[1920];
float zbuf[1920];
buf[]stores ASCII characterszbuf[]stores depth values (Z-buffer)
This allows proper 3D overlap rendering.
A (p, q)-torus knot is the curve:
x(t) = (R + a cos(qt)) cos(pt)
y(t) = (R + a cos(qt)) sin(pt)
z(t) = a sin(qt)
For p=2, q=3 this traces a trefoil knot.
For every value of t along the curve we compute:
- the tangent vector
T(t)(derivative of the curve) - two perpendicular unit vectors
N,Bforming the cross-section plane - a circle of radius
tubearound the curve in that plane
This sweeps a smooth tube along the knot.
Rotation variables:
A += 0.04;
B += 0.02;
control rotation over time.
Trigonometric functions:
sin()
cos()
are used to rotate 3D coordinates around the X and Y axes each frame.
Perspective projection converts 3D points into terminal coordinates:
int xp = WIDTH / 2 + K1 * ooz * x2;
int yp = HEIGHT / 2 - K1 * 0.5 * ooz * y2;
where ooz = 1 / (z + CAM_Z) creates the illusion of depth.
if (ooz > zbuf[o])
ensures closer points overwrite farther points.
This prevents rendering artifacts where the knot winds in front of itself.
Brightness values are mapped to characters:
".,-~:;=!*#$@"
Different characters simulate lighting intensity.
Example:
. -> dark
@ -> brightest
The brightness comes from the dot product of the rotated surface normal with a fixed light direction.
The frame is drawn using:
putchar()
and terminal cursor reset escape sequences:
printf("\x1b[H");
This redraws frames in-place to create animation.
Compile using:
gcc knot.c -o knot -lm
Or just:
make
The -lm flag links the math library.
./knot
Edit these constants at the top of main() in knot.c:
const float p = 2.0f, q = 3.0f; // try 3/2, 3/5, 5/2, 5/3 ...
const float R = 1.0f, a = 0.5f;
const float tube = 0.18f;
p=2, q=3-> trefoil knotp=3, q=2-> also a trefoilp=3, q=5-> cinquefoilp=5, q=2-> Solomon's seal knot
gcd(p, q) must equal 1 for the curve to be a single knot rather than several linked circles.
!!!
;~::;~:;==
:::.....-;;;;
~-.. =!!.:;;;!!
~=!=;;:-~:-:::;=!!
:=;;::~,. .--, ,!!:
;;;,~;=!= .... :!!:
::, ,:-:!!!.., !!!:-
~~-~ -;=======~:~
.-~::;=!!.~::::~.
.,:::;=~.
(The actual knot rotates continuously in the terminal.)
- Parametric curves
- Tube geometry along a curve
- Orthonormal frames in 3D
- 3D coordinate systems
- Perspective projection
- Rotation matrices
- Trigonometry in graphics
- ASCII rendering
- Frame buffering
- Z-buffering
- Animation loops
- Terminal graphics
- Real-time rendering
Standard C libraries only:
stdio.hmath.hstring.hunistd.h
No graphics engine required.
Inspired by the classic ASCII donut demo created by Andy Sloane.
A torus knot is a sibling of the torus — instead of wrapping once around the donut, the curve wraps p times one way and q times the other before closing on itself.