Visualizing distributions with pepperoni pizza (and javascript)

Monday, August 18, 2025

There's a pizza shop near me that serves a normal pizza. I mean, they distribute the toppings in a normal way. They're not uniform at all. The toppings are random, but not the way I want.

The colloquial understanding of "random" is kind of the Platonic ideal of a pizza: slightly chaotic but things are more or less spread out over the whole piece in a regular way. If you take a slice you'll get more of less the same amount of pepperoni as any other slice. And every bite will have roughly the same amount of pepperoni as every other bite.

I think it would look something like this.

This pizza to me is pretty much the canonical mental pizza. It looks pretty random, but you know what you're gonna get. And it is random!

Here's how we made it, with the visualiztion part glossed over. First, we make a helper function, since Math.random() gives us values from 0 to 1, but we want values from -1 to 1.

// return a uniform random value in [-1, 1]
function randUniform() {
  return 2*Math.random() - 1;
}

Then, we make a simple function that gives us the coordinates of where to put a pepperoni piece, from the uniform distribution.

function uniformPepperoniPosition() {
  var [centerX, centerY, radius] = pepperoniBounds();

  let x = radius*2;
  let y = radius*2;

  while (x**2 + y**2 >= radius**2) {
    x = randUniform() * radius;
    y = randUniform() * radius;
  }

  return [x+centerX, y+centerY];
}

And we cap it off with placing 300 fresh pieces of pepperoni on this pie, before we send it into the oven. (It's an outrageous amount of very small pepperoni, chosen in both axes for ease of visualizing the distribution rather than realism.)

function drawUniformPizza() {
  drawBackground();
  drawPizzaCrust();
  drawCheese();

  var [_, _, radius] = pepperoniBounds();

  for (let p = 0; p < 300; p++) {
    let [x,y] = uniformPepperoniPosition();
    drawPepperoni(x, y);
  }
}

But it's not what my local pizza shop's pizza's look like. That's because they're not using the same probability distribution. This pizza is using a uniform distribution. That means that for any given pepperoni, every single position on the pizza is equally likely for it to land on.

These are normal pizzas

We are using a uniform distribution here, but there are plenty of other distributions we could use as well. One of the other most familiar distributions is normal distribution. This is the distribution that has the normal "bell curve" that we are used to seeing. And this is probably what people are talking about most of the time when they talk about how many standard deviations something is away from something else.

So what would it look like if we did a normal distribution on a pizza?

The very first thing we need to answer that is a way of getting the values from the normal distribution. This isn't included with JavaScript by default, but we can implement it pretty simply using the Box-Muller transform. This might be a scary name, but it's really easy to use. Is a way of generating numbers in the normal distribution using number sampled from the uniform distribution. We can implement it like this:

function randNormal() {
  let theta = 2*Math.PI*Math.random();
  let r = Math.sqrt(-2*Math.log(Math.random()));

  let x = r * Math.cos(theta);
  let y = r * Math.sin(theta);

  return [x,y];
}

Then we can make a pretty simple function again which gives us coordinates for where to place pepperoni in this distribution. The only little weird thing here is that I scale the radius down by a factor of 3. Without this, the pizza ends up a little bit indistinguishable from the uniform distribution, but the scaling is arbitrary and you can do whatever you want.

function normalPepperoniPosition() {
  var [centerX, centerY, radius] = pepperoniBounds();

  let x = radius*2;
  let y = radius*2;

  while (x**2 + y**2 >= radius**2) {
    [x,y] = randNormal();
    x = x * radius/3;
    y = y * radius/3;
  }

  return [x + centerX, y + centerX];
}

And then once again we cap it off with a 300 piece pepperoni pizza.

function drawNormalPizza() {
  drawBackground();
  drawPizzaCrust();
  drawCheese();

  for (let p = 0; p < 300; p++) {
    let [x,y] = normalPepperoniPosition();
    drawPepperoni(x, y);
  }
}

Ouch. It's not my platonic ideal of a pizza, that's for sure. It also looks closer to the pizzas my local shop serves, but it's missing something...

See, this one is centered around, you know, the center. Theirs are not that. They're more chaotic with a few handfuls of toppings. What if we did the normal distributions, but multiple times, with different centers?

First we have to update our position picking function to accept a center for the cluster. We'll do this by passing in the center and generating coordinates around those, while still checking that we're within the bounds of the circle formed by the crust of the pizza.

function normal(cx, cy) {
  var [centerX, centerY, radius] = pepperoniBounds();

  let x = radius*2;
  let y = radius*2;

  while ((x-centerX)**2 + (y-centerY)**2 >= radius**2) {
    [x,y] = randNormal();
    x = cx + x * radius/3;
    y = cy + y * radius/3;
  }

  return [x, y];
}

And then instead of one single loop for all 300 pieces, we can do 3 loops of 100 pieces each, with different (randomly chosen) centers for each.

function drawClusterPizza() {
  const settings = initializeCanvas("drawing-3");

  drawBackground(settings);
  drawPizzaCrust(settings);
  drawCheese(settings);

  var [centerX, centerY, radius] = pepperoniBounds(settings);

  for (let c = 0; c < 3; c++) {
    let [cx, cy] = uniform(settings, centerX, centerY, 1);
    console.log(cx, cy);

    for (let p = 0; p < 100; p++) {
      let [x, y] = normal(settings, cx, cy, 4);
      drawPepperoni(settings, x, y);
    }
  }
}

That looks more like it. Well, probably. This one is more chaotic, and sometimes things work out okay, but other times they're weird. Just like the real pizzas. Click that "regenerate" button a few times to see a few examples!

Okay, but when do you want one?

So, this is all great. But, when would we want this?

I mean, first of all, boring. We don't need a reason except that it's fun! But, there's one valid use case that a medical professional and I came up with[1]: hot honey[2].

The ideal pepperoni pizza just might be one that has uniformly distributed pepperoni with normally distributed hot honey or hot sauce. You'd start with more intense heat, then it would taper off as you go toward the crust, so you maintain the heat without getting overwhelmed by it.

The room to play here is endless! We can come up with a lot of other fun distributions and map them in similar ways. Unfortunately, we probably can't make a Poisson pizza, since that's a distribution for discrete variables.


  1. I really do talk about weird things with all my medical providers. And everyone else I meet. I don't know, life's too short to go "hey, this is a professional interaction, let's not chatter on and on about whatever irrelevant topic is on our mind."

  2. The pizza topping, not my pet name.


Please share this post, and subscribe to the newsletter or RSS feed. You can email my personal email with any comments or questions.

If you're looking to grow more effective as a software engineer, please consider my coaching services.


Want to become a better programmer? Join the Recurse Center!
Want to hire great programmers? Hire via Recurse Center!