Draw My Canvas / studio

Apollonian Gasket

Drop the biggest circle that fits into every gap. Repeat. A 2,200-year-old construction, turning.

Plate 13circle-packing

Waiting for the animation above to load…

What lands in the file, and what the width does

One click writes whatever the animation is drawing at that moment to a PNG, with the drawmycanvas.com mark drawn into the picture rather than laid over it. Leave the width box empty and you get the stage exactly as your browser rasterised it — your window’s width times its device pixel ratio, which is about 1,600 px across from a 1280‑px window on a HiDPI laptop and about 353 px from a 390‑px phone.

Type a width instead, or take a preset, and the frame is redrawn into a canvas that wide: the height follows the stage’s own shape and the mark scales with it. The stage is 16:9 on a wide window and 4:3 below 560 px, so a width of 1200 saves 1200×675 on a laptop and 1200×900 on a phone. 1200 px is the width Open Graph and X link cards are cut from — the canonical card is 1200×630, and a card crops the extra height rather than letterboxing it.

Honest limits. Asking for more pixels than the stage was drawn at resamples pixels that were never drawn: a 1920‑px file exported from a 353‑px phone stage is bigger, not sharper. For a big file that is sharp, use Save as wallpaper: it draws the plate again from scratch at exactly 1179×2556 or 1290×2796 (phones), 1920×1080, 2560×1440 or 3840×2160 (4K), so every line is rasterised at that size and the shape is the screen’s, never stretched. Because the plate restarts, a wallpaper is a fresh run of it rather than the exact frame on screen; it is given as long as the stage has been running, up to 20 seconds, to develop. A very large width is a real memory allocation and a browser is allowed to refuse it; when that happens the line above says so plainly and nothing else on the page changes. Stills are PNG only — no JPEG, no WebP. On browsers that can record video, Record a clip saves 5, 10 or 20 seconds of the running animation as MP4 or WebM (whichever this browser can encode) at the same width, with the mark in every frame. A clip is not a seamless loop, it has no audio, and a width bigger than the stage is resampled rather than sharper — only the wallpaper is redrawn at its size. Where a browser cannot record video the button never appears and a PNG is the only export. And nothing is uploaded: the frame or clip is assembled in your own browser, so no frame of this plate ever reaches us.

Live on an HTML canvas · vanilla JavaScript · no dependencies Open fullscreen

About this piece

Three circles that all touch each other, sitting inside a fourth that they all touch from within. That leaves four gaps. Drop the largest circle that fits into each gap; it necessarily touches all three of its neighbours, and leaves three smaller gaps behind. Repeat forever and you have an Apollonian gasket, named for Apollonius of Perga, who set the underlying tangency problem around 200 BC.

What is left when you have removed every circle has zero area and is still uncountably infinite — a fractal dust with Hausdorff dimension about 1.3057. That number has no closed form anyone has found; it is a numerical result.

How the circles are found

Descartes’ circle theorem relates the curvatures of four mutually tangent circles. Curvature is 1/radius, and it is negative for the one the others sit inside — that sign is the whole trick that lets the outer boundary fall out of the same arithmetic as everything else:

(k₁ + k₂ + k₃ + k₄)² = 2(k₁² + k₂² + k₃² + k₄²)

Solve that quadratic for k₄ and you get two roots, both of them real circles: the gap circle you want, and the one you already had. So this piece never solves it. Given a tangent quadruple, the other circle tangent to any three of them is just the reflection of the fourth — and the identical relation holds for the products of curvature and centre, so the position comes out the same way:

k′ = 2(k₁+k₂+k₃) − k₄
k′z′ = 2(k₁z₁+k₂z₂+k₃z₃) − k₄z₄

No square root, so there is no branch to pick and nothing for floating point to drift on. Every circle you can see came out of those two lines, and it holds up: run the recursion down to radius 0.006 of the disc and it yields 329 circles, of which the one reaching furthest out has |centre| + radius = 1.000000000000001. After twenty levels of feedback, double precision is off by one part in 1015.

What to look for

The packing is built once and cached; a frame is only a rotation and a re-stroke of the same table, plus one slow brightness wave running outward through the recursion depths. Building that table for a 1280×900 window takes about 2 ms and yields 911 circles. Doing it sixty times a second — 120 ms of arithmetic per second, for a table whose contents cannot have changed — is the difference between a plate you can leave running on a phone and one you cannot.

Depth is capped by a minimum on-screen radius of about one pixel, not by a fixed level. A 390×844 phone therefore builds 329 circles where a 1280×900 desktop builds 911: a shallower gasket rather than the same one rendered badly. Widen the window and new circles genuinely appear.

Honest limits: this is the symmetric gasket, seeded from three equal circles. Apollonian packings exist for any tangent starting triple, and the lopsided ones are arguably more interesting to look at — they just do not sit as well inside a rectangular frame you did not choose. And because the disc is round and your iframe probably is not, there is empty paper at the sides on a wide window. Reduced-motion visitors get the packing drawn once, held still.

Curious how the loop and canvas fit together? Read how it works →

More from the gallery

All 32 animations in the gallery →