Skip to main content

Command Palette

Search for a command to run...

I Built a Mini 3D Rendering Engine Using Just Canvas & Math

Most people use libraries like Three.js or WebGL to render 3D graphics on the web.

Updated
4 min read
I Built a Mini 3D Rendering Engine Using Just Canvas & Math

I didn’t.

Instead, I decided to reinvent the wheel — and honestly, it was one of the most fun and eye-opening things I’ve done.

In this article, I’ll walk you through how I built a fully interactive 3D cube using just the HTML Canvas and math — no 3D frameworks at all.

Live Demo: click here
Source Code: GitHub repo


🎯 Why I Did This

I wanted to understand:

  • How 3D actually works behind the scenes

  • How objects are rotated in space

  • How 3D coordinates become 2D pixels

And the best way to learn that?

👉 Build it yourself.


🧊 Step 1: Representing a Cube in 3D

A cube is just 8 points in 3D space.

https://images.openai.com/static-rsc-4/blYXcL9_A54k0LVevrogzTREz_NJN_PVqt3XJ-vvpNoiyeundxpPH4cfJ_hAK7x6kv4DzOOv9sPzY-sSwbi-C-Ki6X-Yig_T4DsvrfIYwkHx6PgzrMprr67BEL9gr3E_VXX7fC9pVruDTNg-zk-9JO4Rpg4WS19lyo6GCXrWquKr3z_YAtLeZQtB-OvAScg_?purpose=fullsize

In my code, I defined them like this conceptually:

  • Each vertex has (x, y, z)

  • Values go from -25 to 25 to center the cube

So imagine something like:

  • Front face → z = +25

  • Back face → z = -25

This gives us a cube centered around the origin.


🔗 Step 2: Connecting the Dots (Edges)

Points alone don’t make a cube — we need lines.

So I defined connections between vertices like:

  • Top square

  • Bottom square

  • Vertical edges

This creates a wireframe cube.

https://images.openai.com/static-rsc-4/Y1tO6XyLuUCTjd_LSYVKALbQbhY2G3qXDPuaRqvvRsRERuTarRP76121YBnHKMFyP76SRNOjxP3EzjyyK2KwXb1b7IVszC-rHsluE2NevaBWyWmbCcQMw70l0oXogYEGEGqq7k9vSKiageH07kgBpYCNz0xzpu3oOtV9ZLFwIwpWbA6fl6VdI3cN9hjt6HQW?purpose=fullsize

🔄 Step 3: Rotating the Vector

This is where things get interesting.

To rotate a point in 3D, we use rotation formulas.

For example, rotating around one axis:

  • Mixes two coordinates

  • Keeps one unchanged

In my implementation, I rotated points step by step:

  1. Rotate in one plane

  2. Then another

So each vertex gets transformed like:

👉 original → rotated → moved → projected

This is handled by a transformation pipeline in my code.

https://images.openai.com/static-rsc-4/KUth7Hjj1QafYr3kBwaeL3dSsX1JqxOlPAnfBVGqC6w0zP9l_Fw2zv_Klckz5uNHLGOVhpFFmCEMrqZnW5fvPCzH2ZcoRBpMwPs2DI2kn5QmZ4k4JHNtVbzS0RylOqS8mkc18jFUGgVhAHuKBbTXTDCgWRv4QIRLaJ5oB3bdS3W5BmaEolpZwxCy-bJGjY5J?purpose=fullsize

📏 Step 4: Converting 3D → 2D (Projection)

This is the most important part.

We can’t display 3D directly on a screen — we need to project it into 2D.

The magical formula:

  • x' = x / z

  • y' = y / z

This creates a perspective effect:

  • Far objects look smaller

  • Near objects look bigger

https://images.openai.com/static-rsc-4/KcUb8rumeP2R3fGQhENkKRzOJHvxKfRr1_6upn2ex3KL7XDY3jQWCy7SaC4nvZ6BnyvP_IPLmbBmr3YZZJ4ZbqbYzRKtw_bWVO4c5XwoD2gnCMeWs06jvJTOauvu2VAAu82L-uJjY1FM1ibZWmfdVnbRyzkPV--hIv8j3bBHazdBHXQdFS9W9GCUwXHJNnSP?purpose=fullsize

🖥️ Step 5: Mapping to Screen Coordinates

After projection, values are between -1 and 1.

But the canvas needs pixel values.

So I mapped them like:

  • Center → middle of canvas

  • Scale → fit entire cube

This step converts math into actual pixels.


🎨 Step 6: Drawing on Canvas

Now comes the fun part — rendering.

I built small helper functions:

  • Draw a point

  • Draw a line

  • Fill a face

Then for each frame:

  1. Transform all vertices

  2. Project them

  3. Draw them


🟦 Step 7: Adding Colors (Faces)

A cube has 6 faces.

To make it look solid:

  • I grouped vertices into faces

  • Assigned each face a color

  • Drew them in order

But there’s a catch 👇


🧠 Step 8: Depth Sorting (Painter’s Algorithm)

If you draw faces randomly, they overlap incorrectly.

https://images.openai.com/static-rsc-4/Ftxdu6qmyvO60egjaQqT5qpe_sU_aqFBeRIBhL6adbbNCAqXZvAj1_e-Cq6ATDqjC74pEYEgfyH6L9tNouUnh3lPfno_DJ2bliP1O7DMZN2n_cJ_yPu6knEcWLfW7vA0pLNaU3zc9Jk8f3YDktewUG_Z9Sa7TJu57XPc5YwXSf4fv7f3HRGZFet6hnbuZ57p?purpose=fullsize

So I:

  1. Calculated average depth (z) of each face

  2. Sorted them from far → near

  3. Drew them in that order

This creates a proper 3D illusion.

https://images.openai.com/static-rsc-4/zaZUVWR2ybYSswEqjA_kgR9kgaTuxMLSV7eCy5A_9zKNvxbMsGX8Oa2ppGUYhkx0Mcztv0HoibRv1yIFSeyebsPtEpfhfqNSw5OkR6-U3fBuCkHBgwjIfhbOsL2by77bexYizZkDfDSV3H2_jYDhOOi9NUm2t6WF9Qp5WSyGU9HmV2y-obEDJHjQoeh7UkAQ?purpose=fullsize

🖱️ Step 9: Interaction (Drag to Rotate)

I added mouse + touch support:

  • Drag left/right → rotate horizontally

  • Drag up/down → rotate vertically

When not dragging:

  • Cube slowly auto-rotates

This makes it feel alive.


⚙️ Step 10: Customization Panel

One of the coolest parts I built:

👉 A UI to edit the cube itself

You can:

  • Add/remove vertices

  • Change coordinates

  • Define edges manually

This turns the cube into a mini 3D editor.


🔁 Step 11: The Render Loop

Everything runs inside a loop:

  • Clear canvas

  • Transform vertices

  • Draw cube

  • Repeat

This creates smooth animation (~60 FPS).


💡 What I Learned

This project taught me:

  • 3D is just math (vectors + transformations)

  • Perspective is just division

  • Rendering = drawing in the right order

  • Libraries like Three.js hide a LOT of complexity


🚀 Final Thoughts

Reinventing the wheel is often discouraged…

But sometimes, it’s the best way to truly understand things.

If you’re learning graphics, I highly recommend trying this.

Not for production.

But for clarity.