Draw My Canvas / studio

Metaballs

Metaballs are blobs that melt into each other. Each of the seven coral dots is a charge, and every charge adds r²/d² to one shared field. The blue shapes are wherever that field reaches 1. A lone charge makes a circle, but two charges start to merge while still nearly three radii apart, because their fields add up in the gap. The outline is not drawn with circles at all: it is found by marching squares on a grid of 11‑pixel cells, with each crossing placed by linear interpolation.

Plate 41implicit-surface

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

Seven charges drift across the plate on slow Lissajous paths, each with its own two periods between 17 and 37 seconds, so the picture never quite repeats. Each charge has a radius. Together they make one scalar field, and the plate draws two of its level lines: a filled outline where the field equals 1 and a fainter halo where it equals 0.5. The coral dots are the charges themselves. The faint squares hugging each outline are the grid cells the outline passes through. Those are the cells marching squares actually had to work on (about 270 of the 8,000 or so on a desktop-sized stage). The line along the bottom counts the charges, the separate outlines at f = 1, their vertices and the grid. With reduced motion switched on, you get one still frame, held.

The field: every charge adds r²/d²

The Wikipedia article on metaballs (read 13 October 2026) credits the technique to Jim Blinn in the early 1980s, “to model atom interactions for Carl Sagan’s 1980 TV series Cosmos”. Blinn wrote it up as “A Generalization of Algebraic Surface Drawing” (ACM Transactions on Graphics 1(3), 1982). His version used a Gaussian. This plate uses the inverse-square form, one of the variants the article lists:

f(p) = Σᵢ rᵢ² / |p − cᵢ|²        cᵢ = centre, rᵢ = radius of charge i
inside  ⇔  f(p) ≥ 1

Three worked numbers explain everything you see:

  • A lone charge is a circle of radius r. With one charge, f = 1 exactly when the distance d = r. Its halo at f = 0.5 is the circle of radius r√2, about 1.41 r.
  • Two blobs melt together before they touch. Put two charges of radius r a distance d apart. At the midpoint each contributes r²/(d/2)², so f = 8r²/d². That reaches 1, and a neck opens between them, at d = 2√2 r ≈ 2.83 r. Two plain circles of radius r would only touch at d = 2 r. When they do get that close, the midpoint holds f = 2, well inside one merged shape.
  • Halos join earlier still. The halo’s neck opens at 8r²/d² = 0.5, which is d = 4 r. That is why the faint shapes bridge across gaps the solid ones have not crossed yet.

The field never reaches zero. Every charge pulls a little on every point of the plate, so a blob bulges towards a neighbour well before they join. That bulge is what makes metaballs look soft instead of like overlapping discs.

Marching squares: 16 cases

There is no formula for the outline itself, so it is found on a grid. The field is sampled at the corners of square cells 11 CSS pixels wide, and every corner is marked in (f ≥ 1) or out. Following the Wikipedia description of marching squares, the four bits are read clockwise from the top-left corner, most significant first. That gives a cell index from 0 to 15. Each index fixes which of the cell’s edges (top, right, bottom, left) the outline crosses:

index = 8·TL + 4·TR + 2·BR + 1·BL

 0  none                    8  TL        left–top
 1  BL        left–bottom    9  TL BL     top–bottom
 2  BR        bottom–right  10  TL BR     saddle
 3  BL BR     left–right    11  TL BR BL  top–right
 4  TR        top–right     12  TL TR     left–right
 5  TR BL     saddle        13  TL TR BL  right–bottom
 6  TR BR     top–bottom    14  TL TR BR  left–bottom
 7  TR BR BL  left–top     15  all       none

The two saddles. In cases 5 and 10 two diagonal corners are in and two are out. The bits alone cannot tell whether the in corners join through the middle of the cell or are two separate blobs. Following Wikipedia, the plate takes the cell-centre value to be the average of the four corners and decides with that. Worked example: corners TL 0.7, TR 1.6, BR 0.9, BL 1.3 give index 4 + 1 = 5. The centre value is (0.7 + 1.6 + 0.9 + 1.3)/4 = 1.125, which is at least 1, so TR and BL are joined. The cell gets the two segments left–top and bottom–right, which cut off the two out corners. With a centre below 1 it would get top–right and left–bottom instead, cutting off the in corners. Saddles are rare here. Measured over 600 seconds of this plate at a 1280‑pixel-wide stage, 15 saddle cells came up in 1,200 sampled frames, roughly one every 80 frames. They happen exactly where a neck is forming or snapping.

Linear interpolation. A crossing is not put at the middle of an edge. If the edge’s two corners hold a and b, the outline crosses it at t = (1 − a)/(b − a) of the way from a to b. Corners of 0.6 and 1.4 put it halfway. Corners of 0.8 and 2.0 put it at 0.2/1.2 = 0.167, under 2 pixels into an 11‑pixel cell. Without this step every outline would be a staircase of half-cells. With it, a lone charge of radius 80 px is traced as a 56-sided polygon whose vertices sit within 0.5 px of the true circle.

Closing the loops. The outer ring of grid corners lies just off the canvas and is set to zero, so a blob drifting off the edge still gets a closed outline. Every crossing point is then shared by exactly two segments, one in each cell on either side of its edge. The plate chains them into loops and fills the loops with the even-odd rule, so a pocket enclosed by three blobs comes out as a hole.

How it is computed

The picture is a pure function of the plate’s age. The charges’ positions come straight from the clock, and the field and both contours are recomputed from scratch every frame. That takes about 0.6 ms in Node for a desktop-sized stage. The frame rate decides how often the picture is redrawn, never what is in it. The math lives in one block of code, used by the gallery tile, the embed and a node module that the test suite loads. The suite checks that the field equals Σ r²/d² at 1,000 seeded points. It checks that every contour vertex lies on its threshold (within 10⁻⁶) of the linear interpolation along its edge, and that every loop closes. It compares the 16-case table, both saddles included, against a literal written out by hand. In a padded grid it checks that a saddle cell’s centre ends up inside the filled outline exactly when its centre value is at least 1. A copy of the table with one saddle flipped turns the suite red. The three copies of the code are compared line for line. Every colour comes from the site’s palette in the current theme: blobs in cobalt, halo in the deeper cobalt, charges in coral.

A short, runnable marching-squares snippet, with all 16 cases written out, is on Contour Drift. That plate draws the same algorithm as a topographic map of drifting noise. Swap its noise for the sum above and keep one level, 1, and it draws metaball outlines.

The plates it sits nearest

  • Contour Drift uses the same marching squares at twelve thresholds over drifting value noise, drawn as contour lines instead of filled shapes.
  • Cosmic Circles draws soft orbs as separate radial glows that only overlap. Here the softness is a single shared field, which is why the shapes join instead of stacking.
  • Voronoi Drift also has drifting points, but every pixel belongs to its nearest point only. Here every point counts at every pixel.

Honest limits

The outline is exact only on the grid. Linear interpolation assumes the field changes in a straight line along each edge, and r²/d² does not. On a round blob the error is under a pixel. In a thin neck about to snap it is not: over the run measured above, the true field at a drawn vertex was off from 1 by up to 16%. The vertex lies exactly on the threshold of the interpolated field, not of the true one.

Features smaller than a cell can vanish. A neck thinner than about one 11‑pixel cell can fall between two sample corners and be missed, so two blobs can look joined a frame early or late. The saddle rule only guesses with the mean of four corners. It is a good guess, but not the field’s value at the centre.

Every charge is summed at every corner. Inverse-square charges have no cut-off radius, so the cost is charges × grid corners each frame, about 57,000 terms here. That is fine for seven charges and would not scale to thousands. Production metaballs use a falloff that reaches zero at a finite radius, so only nearby charges need to be summed.

Two dimensions only. The 3D version, marching cubes, has 256 cases, and its ambiguous faces are a harder problem than these two saddles.

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

More from the gallery

All 44 animations in the gallery →