Thousands of stipple dots become one unbroken pen line, untangled by 2-opt.
A subject is first rendered as a density image: a marble bust ray marched from signed distance functions with soft shadows and ambient occlusion, a sunflower drawn with canvas paths, or a word you type. Weighted Voronoi stippling scatters up to ten thousand dots by darkness and runs Lloyd relaxation, every dot moving to the weighted centroid of its cell, with the weights raised to a power so that the finished line lays down ink in proportion to tone. A greedy pen then joins the dots nearest first, leaving long jumps, and 2-opt and Or-opt moves with neighbor lists and don't-look bits flash red as they untangle it. A last sweep checks every pair of edges for crossings, so the drawing always ends as one closed line that never crosses itself, inked on cream paper.
Try it. Type a word to draw it (it starts after a short pause, or press Enter). Drag sideways to choose how many dots, click to redraw the line from a new starting dot, use the buttons or the left and right arrows to switch between the bust, the sunflower and your word, and the up and down arrows for more or fewer dots.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build a continuous-line drawing generator (TSP art) with JavaScript and the HTML canvas element. Put everything in a single index.html file with no libraries or build step, so I can open it directly in a browser.
Start simple:
- Make a canvas that fills the window, stays sharp on high-DPI screens (scale by devicePixelRatio), and is painted a warm cream like paper.
- Draw a big bold word on a small offscreen canvas (about 400 by 200 pixels) with a dark gradient fill, read it back with getImageData, and turn each pixel's darkness into a density between 0 and 1.
- Scatter about 3,000 dots, placing each one at a random pixel chosen with probability proportional to density.
- Improve them with weighted Lloyd relaxation: assign every pixel to its nearest dot (a coarse grid of dots makes this fast), then move each dot to the density-weighted average of its pixels. Repeat about 30 times, drawing the dots as they settle.
- Then join every dot into one path with a greedy nearest neighbor tour and draw it as a thin dark ink line.
Once that works, make it beautiful:
- Improve the tour with 2-opt: if two edges A-B and C-D would be shorter as A-C and B-D, reverse the path between them. Only try C among each dot's eight nearest neighbors, and run it a few milliseconds per frame so the line visibly untangles.
- Flash the edges each move creates in red for a moment.
- Let me type a new word to draw, and show the tour length dropping.
Explain the key ideas in short code comments. When you're done, tell me how to open it and suggest three directions I could take it next, such as Or-opt moves, a ray marched 3D bust as the subject, or varying the line width with the local dot density.