Let’s see how to create a Spinning 3D Donut Animation using only ASCII characters in C++. The goal is to simulate a 3D donut, rotate it around two axes, and display it using ASCII characters. The donut’s surface is calculated in 3D and then projected into 2D for the terminal display.

Steps to Build the Animation
Setup and Variables
To begin, we need to define a few key variables:
- alpha and beta are the angles that control the rotation of the donut around two axes.
- frame_buffer stores the ASCII characters that represent the donut’s surface.
- depth_buffer keeps track of the depth (distance from the viewer) for each point on the donut.
float alpha = 0, beta = 0;
float frame_buffer[1760], depth_buffer[1760];
The Animation Loop
The animation loop runs continuously to update and display each frame of the donut.
a. Clear Buffers
Before each new frame, we reset both the frame_buffer (filled with spaces) and the depth_buffer (reset to zeros).
memset(frame_buffer, 32, 1760);
memset(depth_buffer, 0, 7040);
b. Donut Points Calculation
The donut’s surface is calculated using trigonometric functions (sin and cos), which generate 3D coordinates. These coordinates are based on two variables, i and j, which define the positions of the points around the donut.
x = (R2 + R1 * cos(i)) * cos(j);
y = (R2 + R1 * cos(i)) * sin(j);
z = R1 * sin(i);
Here, R1 and R2 are the radii of the donut, and x, y, z represent the 3D coordinates of each point on the donut’s surface.
c. Apply Rotation
To make the donut spin, we rotate the 3D points around the vertical (alpha) and horizontal (beta) axes.
x = x * cos(alpha) - z * sin(alpha); // Rotate around vertical axis
z = x * sin(alpha) + z * cos(alpha); // Update z-coordinate
d. Projection to 2D
To display the donut in the terminal, we convert the 3D coordinates into 2D using a perspective projection.
x_proj = x / z;
y_proj = y / z;
This simulates perspective, making points closer to the viewer appear larger.
e. Shading
We calculate the brightness of each point based on its angle to the light source.
brightness = (sin(i) * cos(j) + cos(i) * sin(alpha) + cos(j) * sin(beta));
This formula calculates a brightness value that’s used to pick an appropriate ASCII character, such as @ or ..
Display the Frame
After all points are calculated and shaded, we update the frame_buffer with the corresponding ASCII characters. The characters are printed in the terminal to create the spinning donut effect. The cursor is moved to the top-left corner before printing each frame to overwrite the previous one.
for (int k = 0; k < 1761; k++) {
putchar(k % 80 ? frame_buffer[k] : 10); // Print line by line
}
Rotation and Speed
To make the donut spin smoothly, the angles alpha and beta are slightly incremented after each frame.
alpha += 0.04;
beta += 0.02;
The usleep(30000) function adds a short delay to control the animation speed, giving a smooth rotation effect.
Key Concepts in the Code
3D to 2D Projection:
The 3D donut is projected onto a 2D plane using perspective projection, making objects farther away appear smaller and objects closer appear larger.
Shading with ASCII Characters:
ASCII characters (@, #, *, .) are used to simulate the varying brightness of the donut’s surface. Bright points use @ and dark points use ‘.’
Continuous Rotation:
The donut spins continuously as alpha and beta are incremented after each frame, creating the animation effect.
Complete Source Code: Spinning 3D Donut Animation
#include <stdio.h> #include <math.h> #include <string.h> #include <unistd.h> int main() { float alpha = 0, beta = 0; float angle_i, angle_j; int idx; float depth_buffer[1761]; char frame_buffer[1761]; printf("\x1b[2J"); for(;;) { memset(frame_buffer, 32, 1761); memset(depth_buffer, 0, 7040); for(angle_j = 0; angle_j < 6.28; angle_j += 0.07) { for(angle_i = 0; angle_i < 6.28; angle_i += 0.02) { float sine_i = sin(angle_i); float cosine_j = cos(angle_j); float sine_alpha = sin(alpha); float sine_j = sin(angle_j); float cosine_alpha = cos(alpha); float h = cosine_j + 2; float one_over_depth = 1 / (sine_i * h * sine_alpha + sine_j * cosine_alpha + 5); float cosine_i = cos(angle_i); float cosine_beta = cos(beta); float sine_beta = sin(beta); float t = sine_i * h * cosine_alpha - sine_j * sine_alpha; int x_coord = 40 + 30 * one_over_depth * (cosine_i * h * cosine_beta - t * sine_beta); int y_coord = 12 + 15 * one_over_depth * (cosine_i * h * sine_beta + t * cosine_beta); int buffer_index = x_coord + 80 * y_coord; int luminance = 8 * ((sine_j * sine_alpha - sine_i * cosine_j * cosine_alpha) * cosine_beta - sine_i * cosine_j * sine_alpha - sine_j * cosine_alpha - cosine_i * cosine_j * sine_beta); if(22 > y_coord && y_coord > 0 && x_coord > 0 && 80 > x_coord && one_over_depth > depth_buffer[buffer_index]) { depth_buffer[buffer_index] = one_over_depth; frame_buffer[buffer_index] = ".,-~:;=!*#$@"[luminance > 0 ? luminance : 0]; } } } printf("\x1b[H"); for(idx = 0; idx < 1761; idx++) { putchar(idx % 80 ? frame_buffer[idx] : 10); alpha += 0.00004; beta += 0.00002; } usleep(30000); } return 0; }
Conclusion
This spinning 3D donut is an example of how 3D graphics can be rendered using just text. By applying basic trigonometry and perspective projection, you can visualize 3D shapes and create interactive animations in the terminal. Thanks for visiting CodeHelping.
For more such amazing projects visit: Projects Link