Integer Appollonian Packings

I’ve just published a notebook that allows the user to explore several Appollonian packings where the curvature of each circle is an integer:

I’m pretty happy with it but there are a couple of oddities:

  1. You might notice a strange overlap between input and output. This is particularly noticeable if you expand a markdown cell, as in:

    The cause is certainly related to the fact that I’m using Pure CSS.
  2. I found it pretty tricky to get d3.zoom to work properly. The reason is that zooming requires more than just a simple redraw of what’s already on the canvas; you’ve got to actually refine and recompute the picture on zoom-end. That required a reset of the zoom.transform on zoom-end, since I already had my function fully written to work with a specified rectangle not worrying about any transform imposed on the canvas. The final result has a couple of strange properties:
    * Double click is slightly slow to respond since each mouseup fires a zoom-end resulting in two unnecessary redraws. It would be nice to be able to ignore those mouseups.
    * You can zoom in as far as you want, even though there’s a zoom.scaleExtent specified. Naturally, you don’t have to zoom in too far before numerical error becomes apparent.
  1. The cause is certainly related to the fact that I’m using Pure CSS.

Yep, we don’t recommend changing the global styles; it interferes with the layout of cells. Is there any reason to use Pure CSS here? It looks like removing the stylesheet cell fixes the layout problems.

1 Like

Hmm… I rather thought I was following Tom’s instructions here. I’m using Pure CSS to style the button array at the top of the page. The effect is very noticeable on my iPhone. I don’t think it would be super hard to do that without Pure CSS but worrying about how styles work across a variety of devices is near the bottom of my list of concerns. I find that Pure makes that pretty easy.


The problem with frameworks like Pure is that they have a global effect on unstyled elements (such as P elements); it doesn’t just affect parts of your notebook that opt-in to the framework.

There are some libraries that require global styles, but that are opt-in, such as Leaflet and Chartist that Tom gives as an example. But these are still somewhat error-prone; for example these styles have to be careful not to conflict with other style rules, and you can’t have multiple versions of the library on the page simultaneously without their styles conflicting.

The reason that global styles are discouraged on Observable is not just that it breaks the layout of your notebook, but that it also impedes composing (importing) cells across notebooks that use different styles. For example if you were to import a cell from your notebook into another notebook, it wouldn’t look right without the stylesheet; and likewise if you were to import a cell from another notebook into yours, the global style would likely break the display.

I’m on my phone now, but probably you could achieve the desired layout with CSS flexbox, CSS grid, or CSS multi-columns, in a way that would not require much code, would work consistently across modern browsers, and without requiring a global stylesheet that introduces nonstandard behavior for unstyled elements.

1 Like

@mbostock You make great points, of course.

Another option that I’m considering is to remove the Pure CSS link in the notebook but to leave the Pure CSS class names in the code. That way, the CSS will take effect when I embed the cells in my own webpage.

Thanks again Mike!


Digging into the source of your notebook webpages, I notice that your CSS appears to be an extension of the Tachyons CSS framework, which looks a lot like Pure and supports all the things I want to do. I wonder - if I used Tachyons in place of Pure, could I expect better results? For that matter, if I use Tachyons class names in my notebook, is there any chance it would just work as expected?

We use Tachyons in our application, but it’s not exposed to notebooks. We only provide a handful of default styles to notebooks, such as for the body and heading fonts.


To learn more in this direction, check out the book Indra’s Pearls: The Vision of Felix Klein.


Yes, a great, great book! My immediate reference for this work was Beyond the Descartes’ Circle Theorem.




I guess when zooming in far enough, as one of the circular arc becomes almost straight, you’d get floating-point error. I wonder if we can in this case start drawing straight lines instead of circles.

By the way I shared it on Twitter, and people love it! (If I knew your handle I’d have tagged you.)

1 Like

Yeah, I think that numerical error is pretty much inevitable here. You don’t even have to zoom in too far to see it, depending on where you zoom. It’s so fun to explore, though, that I thought I’d leave the ability to zoom in as far as you want.

That’s interesting about Twitter, too. I’ve never used it and really know nothing about it. Perhaps, I should try sometime. :slight_smile:

Thanks a lot!!

1 Like

I think what is being run into here isn’t just inevitable floating point error (which should happen at a much higher zoom level), but some weakness in browser rendering of circles.

If you start playing with very large circles and circular arcs, you’ll get a whole bunch of wonky bugs which are wonky in a different way in every browser. Some browsers will give weirdly quantized shapes. Others will blur out the line. Others will just have the position flop around discretely as you trivially vary parameters.

I agree that rendering a smaller circular arc targeted to the region instead of a gigantic circle with its center far off screen should greatly ameliorate these issues. If possible computing endpoints of the arc using rational arithmetic should reduce rounding errors.

I think this could be substantially improved browser-side if anyone cared enough. Maybe someone should try to talk to browser vendors about improving their C++ (or whatever) code for rendering ellipses and elliptical arcs. They might not be too concerned about these edge cases though; few people try to draw such extreme circles.

To be clear, you’re not talking about playing with circles in the context of just this specific application but more generally, in code that you’ve written – correct?

Interesting speculation. At first I disagreed but it does seem to work better in Chrome over Firefox.

Ultimately, I don’t think this super important. The correct approach would be to restrict zooming. As I mentioned in my original post, though, I haven’t quite figured out how to do that. If I could measure the effective scaleExtent after each zoom-end, I’m pretty sure I could incorporate a zoom restriction. I’m not quite sure how to that, though.

Yeah, if you play with in different browsers you can get some quite strange results. When I made it, Mobile Safari had some especially funny wonky behavior; that is probably still the case but I haven’t checked.





A suggestion: if a number is prime, show it in red!

Do you have a specific reason to think that shading the primes would be interesting? Perhaps something came up on your twitter discussion? (I’m not on twitter so I wasn’t able to find that.) My primary reference for this material is Beyond the Descartes’ Circle Theorem and prime numbers don’t appear to be mentioned there.

Also, can you recommend a high-quality Javascript primality tester? (Fast and accurate for numbers up to 10^10)

Thanks for the interest!

Not really, just likes and retweets. I asked only because that’s what’s on my mind as I was zooming in or dragging around.

I’m aware that this has become a field of number theory, so they are naturally interested in prime numbers. It appears that Peter Sarnak has proved (in 2007) that there are infinitely many primes in an Apollonian packing, and even crazier infinitely many pairs of adjacent “prime circles”! (No triplets though.) That would be fun to see. Maybe color-fill the circle, because the numbers go away while I’m zooming or dragging.

I don’t know about Javascript primality tests, much less high-quality ones.

This prime generator might be of interest:


Well, I finally made myself dig into this, and forked a version with prime numbers. I also tried to make use of BigInt, thinking it would help zooming in deeper. I don’t understand what exactly causes it to stop drawing, when the numbers go in the millions. It may involve more drastic changes to make it work.