Meet Loaf

Our mascot is a loaf of Japanese milk bread, built entirely in code. No model files, no textures, and one toon shader. Here's how it was made, and why it had to read clearly at 16 pixels.

Four frames of Loaf's idle animation: breathing, steam rising, sparkles, and a blink
Four moments from Loaf's three-second idle loop. The last one is mid-blink.

Why a mascot at all

Baker Games used to have a placeholder logo: the letters "BG" on a gradient square. It did the job, but nobody would recognize it in a row of browser tabs, and it didn't say anything about the games. The logos that stick are characters: a face you'd know from across the room, even when it's shrunk to the size of a fingernail.

The name made the brief easy: Baker Games should have a loaf of bread. The hard part was making it look like good bread, warm and glossy and a little smug, rather than clip art. We wanted it to match the games, so we built it the way we build them: in code, with Three.js, using the same tools that render Churn's chess pieces.

Start with the silhouette

A favicon is 16 by 16 pixels. At that size, detail is gone and only the outline and two or three colors survive. So before any shading, we chose the shape, and we chose shokupan, Japanese milk bread, baked in a lidless tin. The tin keeps the bottom straight-sided while the top rises free and mushrooms over the edge. Seen end-on, that's the outline everyone knows as a "slice of toast", and it reads as bread instantly.

It came with a bonus. Shokupan's tin-baked sides stay pale while the free-risen top toasts dark brown. Put a face on the pale end and the dark crown looks like a haircut. The character pretty much designed itself.

The outline is a handful of points: two rounded corners at the base, straight sides, and a superellipse crown, a shape between a circle and a square, so the top looks plump instead of perfectly round. Below the crown's widest point, the same curve tucks back inward to meet the straight sides, and that tuck is the overhang.

// A superellipse crown: squarer than a circle, like bread that rose against nothing.
const x = CROWN_W * Math.sign(c) * Math.abs(c) ** (2 / n)
const y = CROWN_Y + height * Math.sign(s) * Math.abs(s) ** (2 / n)

From outline to loaf

Three.js's ExtrudeGeometry pushes the outline back into a solid block, with a rounded bevel so no edge is sharp. A block looks like a brick, though, and real bread rises unevenly. So after extruding, we walk every vertex and lift the crown more in the middle of the loaf than at the ends, which in a real tin would have pressed against the walls:

// The middle of the loaf rose more than the ends that pressed the tin.
const along = Math.sin(Math.PI * t) ** 0.6
pos.setY(i, y + along * crowned * 0.07)

That one line does more for the "fresh from the oven" feeling than anything else in the model.

Shading it like a painting

Realistic lighting made Loaf look like a product photo. We wanted something closer to a hand-painted cartoon cel, so the material is a custom shader with four ideas in it:

  • Color from where it baked. Below the crown line, the crust is a pale honey color. Above it, it blends to a deep toasted brown, darkest where the surface faces straight up. Two layers of cheap 3D noise add the uneven browning real crust has.
  • One soft shadow edge. Instead of smooth falloff, the light has one terminator, blurred just enough to look painted rather than flat. The shadow side is tinted warm red-brown, not gray, which keeps it looking like food.
  • A rim light in pale cream around the silhouette, so Loaf separates from dark backgrounds.
  • An egg-wash highlight. The glossy shine on the crown is a hard-edged specular highlight, broken up by the same bake noise so it streaks like a real glaze.

The outline is the oldest trick in cel shading, the inverted hull: draw the loaf a second time, pushed slightly outward along its normals, showing only its back faces, in dark brown. Everything behind the real loaf shows through as a crisp ink line.

The face

The first face was pure kawaii: big glossy anime eyes, rosy blush, a little open mouth. It was cute, and it was wrong. Next to a site of chalkboard slate and CRT type, it looked like a sticker someone had left on an arcade cabinet.

The characters we kept pointing at were cute because of their attitude, not instead of it: Rem from Deadlock, Grogu, Cuphead, and Cuphead's Devil. So we built three new faces and set them next to the original:

Four versions of Loaf's face: the kawaii original, a sleepy one with heavy lids, a scheming devil with brows and a fang, and the rubber-hose face that won
Left to right: the kawaii original, sleepy, devil, and rubber hose, the one we kept.

Sleepy was charming but a little too checked out. Devil was fun but read as a villain. Rubber hose won: inked eye whites with pie-cut pupils, the 1930s cartoon tell that Cuphead brought back, under heavy upper lids that make Loaf look confident instead of startled. The smile became a lopsided smirk, and the blush went.

One detail ties it to the rest of the site. Where an anime eye would have a round sparkle, Loaf's highlight is a small rectangle: the reflection of a monitor. Loaf is always looking at a screen.

Size mattered more than style. The first rubber-hose eyes were too small, and at 32 pixels they turned into grey smudges. Scaling them up by about a fifth kept the whites readable in a browser tab, which is where most people will ever see Loaf.

A loop with no seam

Loaf idles on a three-second loop: breathing in and out twice, glancing left and right once, steam drifting up from the crown, sparkles twinkling, and one blink near the end. The rule that makes it seamless is simple: every motion repeats a whole number of times in three seconds. Breathing runs at two cycles per loop, the glance at one, and each steam wisp leaves the crown once per loop with its start offset. When the clock wraps from 3.0 back to 0.0, every part is exactly where it started.

Breathing is squash and stretch, the first rule of cartoon animation. As Loaf gets taller it gets slightly narrower, so it looks like the same amount of bread is moving around:

const breath = Math.sin((tau * t * 2) / LOOP)
loaf.scale.set(1 - breath * 0.018, 1 + breath * 0.035, 1 - breath * 0.018)

Filming it frame by frame

The site doesn't run Three.js just to show a mascot. That would be a lot of code to download for a loaf of bread. Instead we film Loaf once and ship the footage.

Churn already had a tool for this: a script that opens a page in headless Chrome and drives it over the DevTools protocol. It now lives in Baker Engine, our shared engine, and we gave it a new step. The page exposes a frame(t) function that freezes the animation clock at time t, renders exactly one frame, and returns it as a PNG with a transparent background. The script calls it 90 times, from 0 to 3 seconds at 30 frames per second. Because time is set rather than measured, the frames come out identical every time, no matter how slow the machine is.

From those frames, a short shell script and ffmpeg make everything else:

  • the animated WebP on the homepage, with transparency, at 20 frames per second;
  • a still frame that loads first, swapped for the animation once it has downloaded;
  • a separate front-facing render, without steam and sparkles, cropped square for the icons.
Loaf as an icon at 16, 32, 64, and 192 pixels
The same render at 16, 32, 64, and 192 pixels. At 16, it's still a slice of toast with eyes.

What's next for Loaf

We'd like Loaf to start turning up in the games: on loading screens, in a corner of Churn's arena, maybe wearing a wizard hat in Spellbox. Since it's all code, a new pose or outfit is a few lines and a re-render, not a new drawing. If you have an idea for what Loaf should do next, send it in.