The Moon in 10241 Dots — Re-creating a 1969 Masterpiece

Cover image for The Moon in 10241 Dots — Re-creating a 1969 Masterpiece

In 1969, German artist Max Ernst created a beautiful painting in celebration of the Moon-landing:

Original Poster

Entitled Naissance d’une Galaxie (Birth of a Galaxy), I can imagine it must’ve taken months to complete, using stencils for all the dots.

Luckily, we can code it much faster!

Radial Gradient

First, we need a background-layer with a radial background:

main {
  background: #06101D radial-gradient(
    #244D7A 15%, 
    #93A7A2 65%, 
    #234E85 66%, 
    #06101D 70%
  ) no-repeat;

Radial Gradient

Fluffy Clouds Filter

We need some “fluffy clouds” to simulate the painted layers, so we’ll use the “fluffy” filter from my CSS Filter Editor:

We add an ::after-pseudo-element to the same layer as the radial background, inheriting that same background, but also using the “fluffy”-filter:

main {
  &::after {
    background: inherit;
    mix-blend-mode: hard-light;
    content: "";
    display: block;
    filter: url(#fluffy) blur(5px); 
    inset: 0;
    opacity: 0.33;
    position: absolute;

And now we have:

Fluffy Filter


Before we continue with the dots, let’s add an SVG-element in the markup — we’ll add the innerHTML later:

<svg viewBox="0 0 1000 1000" class="moon"></svg>


To hardcode the dots would probably take as long time as the original painting, so let’s use JavaScript and some Math to help us out!

To start with, we need a function, dots(), with a configuration-object:

function dots(config =
    dotsring: 8,
    dotsize: 3,
    radius: 2,
    rings: 50,
    rotate: 6,
    spread: 10,
    x: 1000,
    y: 1000
  }) {


  • dotsring. Dots per ring. This number is added to each new ring.
  • dotsize. The relative dotsize (see SVG viewBox)
  • radius. The start radius-index. In this case, we want to skip the first one, creating a larger “hole” in the center of the moon.
  • rings. The total amount of rings
  • rotate. Amount to rotate each ring
  • spread. The distance between the dots in a ring
  • x. The viewBox x-width of the SVG.
  • y. The viewBox y-width of the SVG.

Next, we need arrays of coordinates for each ring. For that, we need a small helper-method:

const coords = (number) => {
  const frags = 360 / number
  return Array.from({ length: number + 1 }, 
  (_, i) => (frags / 180) * i * Math.PI)


If you inspect the original painting, you’ll notice that most of the dots have the same, light-yellow color. A few, random dots are light blue or lavender.

To help us fill-in random dots with these values, we need a random-method:

const random = (min, max) =>
  Math.floor(Math.random() * (max - min) + min);

… and a fill()-method, where we parse-in the current dot-index as number:

const fill = (number) =>
  ['#74BAA0', '#B3A0C1'][number % random(1, 100)] ||

Rendering the dots

To render the dots, we need two loops:

  • One for the rings. For each ring, we output a <g>-element, to <g>roup all the dots within a ring.
  • One for the dots within a single ring. In this inner loop, we calculate the x and y position of each dot from the coordinates we created using coords().

After that, we simply output a <circle> for each entry.

let s = '';
for (let i = config.radius; i <= config.rings; i++) {
  const r = config.spread * i;
  const ring = coords(config.dotsring * i);
  s += `<g style="--r:${i * config.rotate}deg">${ring
    .map((value, index) => {
      const x =
        config.x / 2 - Math.round(r * Math.cos(value));
      const y =
        config.y / 2 - Math.round(r * Math.sin(value));
      return `<circle cx="${x}" cy="${y}" r="${
      }" fill="${fill(i + index)}" />`;
return s;

Let’s add that output to the SVG-element, we created earlier:

document.querySelector('.moon').innerHTML = dots()

… and voilà:


Could this be done in CSS instead of SVG?.
Yes, using sin() and cos() in CSS, this is definitely possible. I just find it easier to work with SVG and it’s viewBox when working with such large “datasets” as this.

Combining the Layers

Let’s combine the dots with the radial, fluffy-clouds-background:


And we’re done! … but what about the lower part of the painting — below the Moon?

I tried using the drops-filter (from the CSS Filter Editor above) and a bunch of overlaying gradients, but simply couldn’t get the right “look and feel”.

Adding Poster Texts

Let’s turn the painting into a poster, and add some texts.

We’ll use cqi-units for the texts, so they scale nicely to the width of our poster:

Poster Text

Framing the Poster

Posters look better in frames, so let’s add a nice wooden frame!

CSS has a border-style called ridge which does the job — so with:

body {
  border: 1cqi ridge #D39E85;

— we get:

Poster with frame


Finally, it’s time to hang our framed poster on a wall.

Let’s add a wallpaper from CSS Patterns to the <html>-element — and a box-shadow to the <body>-element:


Beautiful, isn’t it? Hope your CPU could manage the 10241 dots!



Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these <abbr title="HyperText Markup Language">HTML</abbr> tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>