Sketch 2026-06-18 13:29

This sketch creates an animated birthday cake with flickering candles and a 'Happy Birthday!' message. The cake features layered frosting with a wavy design, pink candles with animated yellow and orange flames that flicker independently using Perlin noise, and automatically scales to fit any window size.

🧪 Try This!

Experiment with the code by making these changes:

  1. Add more candles — Increasing candleCount adds more flickering flames—great for making the cake feel more festive or for an older birthday.
  2. Make candles flicker faster — Increasing the 0.1 multiplier in the noise function makes flames flicker more rapidly and erratically, like a real candle in wind.
  3. Change the background to pink — The background RGB values control the entire canvas color—this pink tone makes the cake pop more playfully.
  4. Make the cake much wider — Reducing the 0.6 multiplier makes the cake wider relative to the window—try 0.8 for a grand cake that fills more space.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch draws a festive birthday cake complete with frosting, multiple candles, and an animated message. What makes it visually engaging is the flickering candle flames—each one uses Perlin noise to create organic, realistic motion rather than random jittering. The sketch demonstrates key p5.js techniques including responsive sizing (using windowWidth and windowHeight), text rendering, layered shapes, loops to draw repeated elements, and noise-based animation for natural-looking effects.

The code is organized into three functions: setup() prepares the canvas and calculates cake dimensions based on window size, draw() redraws everything 60 times per second and animates the flames, and windowResized() recalculates dimensions when the window changes. By studying this sketch, you'll learn how to make graphics that adapt to any screen, how Perlin noise creates more convincing animation than raw randomness, and how to offset noise values so repeated elements (the candles) animate independently.

⚙️ How It Works

  1. When the sketch loads, setup() creates a canvas filling the entire window and calculates the cake's size as a percentage of the window width (capped at 400 pixels), then centers it slightly below the middle of the screen
  2. Every frame, draw() paints a light blue background and draws the text 'Happy Birthday!' above the cake using a large Georgia font
  3. The cake itself is drawn as a rounded rectangle (cake base) with frosting added on top using two arcs and a connecting rectangle to create a wavy frosting edge
  4. A loop draws five candles evenly spaced across the cake top—each candle is a pink rectangle (the stick) plus two layered ellipses (the flame)
  5. For each candle, Perlin noise generates a flicker value that changes smoothly over time, making the flame width and height pulse organically; the noise is offset by i * 100 so each candle flickers independently instead of in sync
  6. When the window is resized, windowResized() recalculates all cake dimensions so the entire drawing stays proportional and centered

🎓 Concepts You'll Learn

Perlin noise for animationResponsive design with windowWidth/windowHeightLayered shape drawingLoops and arraysFrame-based animationText rendering and alignmentColor and transparency (alpha)

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. It's the perfect place to initialize canvas size, calculate positions, and set drawing defaults that won't change. Notice how this sketch uses responsive sizing (multiplying by width and height) instead of hard-coded numbers—this is best practice for sketches that need to work on any screen.

function setup() {
  createCanvas(windowWidth, windowHeight);
  // Set cake dimensions and position relative to canvas size
  cakeWidth = min(width * 0.6, 400); // Max 400px wide
  cakeHeight = cakeWidth * 0.6;
  cakeX = width / 2;
  cakeY = height / 2 + cakeHeight / 4; // Position slightly below center

  // Set the text properties
  textSize(min(width * 0.1, 80)); // Max 80px
  textAlign(CENTER, CENTER);
  textFont('Georgia'); // A classic font for birthdays
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

calculation Responsive Canvas Setup createCanvas(windowWidth, windowHeight);

Creates a canvas that fills the entire browser window, enabling full-screen responsiveness

calculation Cake Dimension Calculation cakeWidth = min(width * 0.6, 400);

Sets cake width to 60% of window width, but caps it at 400px so it doesn't become too large on huge screens

calculation Cake Centering cakeY = height / 2 + cakeHeight / 4;

Positions the cake horizontally centered and slightly below the vertical center, leaving room for the 'Happy Birthday!' text

createCanvas(windowWidth, windowHeight);
Creates a canvas that stretches to fill the entire window size, allowing the sketch to adapt to any screen
cakeWidth = min(width * 0.6, 400);
Sets the cake width to 60% of the window width, but the min() function ensures it never exceeds 400 pixels, preventing the cake from overwhelming huge displays
cakeHeight = cakeWidth * 0.6;
Makes the cake height 60% of its width, creating a proportional rectangular shape
cakeX = width / 2;
Positions the cake horizontally at the center of the canvas
cakeY = height / 2 + cakeHeight / 4;
Positions the cake slightly below the vertical center, making room above for the birthday text
textSize(min(width * 0.1, 80));
Sets the text size to 10% of window width (capped at 80px) so the 'Happy Birthday!' message scales responsively
textAlign(CENTER, CENTER);
Centers all text both horizontally and vertically around its coordinate, making positioning easier
textFont('Georgia');
Applies Georgia font, a classic serif font that looks elegant and festive for birthday text

draw()

draw() runs 60 times per second, redrawing the entire scene fresh each frame. Notice that we clear the background first, then draw everything else on top—this order is crucial. The candle loop is the most interesting part: Perlin noise creates smooth, organic-looking flicker by outputting values that change gradually rather than jumping around randomly. The key insight is offsetting the noise with i * 100, which gives each candle a completely different sequence of noise values, making them flicker independently rather than in lockstep.

🔬 This loop positions each candle evenly. What happens if you change the spacing formula from (i + 1) * candleSpacing to i * candleSpacing? How does the layout change?

  for (let i = 0; i < candleCount; i++) {
    let candleX = cakeX - cakeWidth / 2 + (i + 1) * candleSpacing;
    let candleY = cakeY - cakeHeight / 2 - candleHeight / 2;

🔬 These lines control how much the flame pulses. What happens if you change the ranges from 0.8-1.2 to 0.5-1.5? How does the flicker intensity increase?

    let currentFlameWidth = map(flicker, 0, 1, flameWidth * 0.8, flameWidth * 1.2);
    let currentFlameHeight = map(flicker, 0, 1, flameHeight * 0.8, flameHeight * 1.2);
function draw() {
  background(240, 250, 255); // Light pastel background

  // Draw the "Happy Birthday!" message
  fill(0, 50, 100); // Dark blue text
  // Position text above the cake
  text("Happy Birthday!", width / 2, cakeY - cakeHeight / 2 - min(width * 0.05, 50));

  // --- Draw the cake ---
  noStroke();

  // Cake base (bottom layer)
  fill(255, 200, 150); // Light brown/cake color
  rect(cakeX - cakeWidth / 2, cakeY - cakeHeight / 2, cakeWidth, cakeHeight, 5); // Rounded corners

  // Frosting (top layer)
  fill(255, 255, 255, 220); // White, slightly transparent frosting
  // Using a series of arcs for a wavy frosting effect
  arc(cakeX - cakeWidth / 2, cakeY - cakeHeight / 2, cakeWidth / 2, cakeHeight / 4, PI, TWO_PI);
  arc(cakeX + cakeWidth / 2, cakeY - cakeHeight / 2, cakeWidth / 2, cakeHeight / 4, PI, TWO_PI);
  // Fill the space between arcs with a rectangle
  rect(cakeX - cakeWidth / 4, cakeY - cakeHeight / 2 - cakeHeight / 8, cakeWidth / 2, cakeHeight / 4, 5);

  // --- Draw candles ---
  let candleSpacing = cakeWidth / (candleCount + 1);
  let candleHeight = cakeHeight / 3;
  let candleWidth = 5;

  for (let i = 0; i < candleCount; i++) {
    let candleX = cakeX - cakeWidth / 2 + (i + 1) * candleSpacing;
    let candleY = cakeY - cakeHeight / 2 - candleHeight / 2;

    // Candle stick
    fill(255, 150, 150); // Pink candle
    rect(candleX - candleWidth / 2, candleY - candleHeight / 2, candleWidth, candleHeight, 2);

    // Candle flame (animated)
    let flameWidth = candleWidth * 1.5;
    let flameHeight = candleHeight * 0.8;

    // Use noise for a more organic flicker
    let flicker = noise(frameCount * 0.1 + i * 100); // Different flicker for each candle
    let currentFlameWidth = map(flicker, 0, 1, flameWidth * 0.8, flameWidth * 1.2);
    let currentFlameHeight = map(flicker, 0, 1, flameHeight * 0.8, flameHeight * 1.2);

    // Yellow core
    fill(255, 255, 0, 200); // Yellow, slightly transparent
    ellipse(candleX, candleY - candleHeight / 2 - currentFlameHeight / 4, currentFlameWidth, currentFlameHeight);

    // Orange outer glow
    fill(255, 165, 0, 150); // Orange, more transparent
    ellipse(candleX, candleY - candleHeight / 2 - currentFlameHeight / 4, currentFlameWidth * 1.2, currentFlameHeight * 1.2);
  }
}
Line-by-line explanation (27 lines)

🔧 Subcomponents:

calculation Background Clear background(240, 250, 255);

Paints a light blue background every frame, erasing the previous frame so animation appears smooth

calculation Birthday Message text("Happy Birthday!", width / 2, cakeY - cakeHeight / 2 - min(width * 0.05, 50));

Draws the birthday text centered above the cake, positioned responsively

calculation Cake Base Shape rect(cakeX - cakeWidth / 2, cakeY - cakeHeight / 2, cakeWidth, cakeHeight, 5);

Draws a tan rounded rectangle as the main cake body

calculation Frosting Decoration arc(cakeX - cakeWidth / 2, cakeY - cakeHeight / 2, cakeWidth / 2, cakeHeight / 4, PI, TWO_PI);

Creates wavy frosting edges using arcs at the left and right sides of the cake

for-loop Candle Drawing Loop for (let i = 0; i < candleCount; i++) {

Iterates through each candle, positioning them evenly spaced across the cake top

calculation Flame Flickering let flicker = noise(frameCount * 0.1 + i * 100);

Generates a smooth, organic flicker value using Perlin noise, offset by candle index so each candle flickers independently

background(240, 250, 255);
Clears the canvas with a light pastel blue (RGB: 240, 250, 255) before drawing each frame, ensuring no trails or ghosting effects
fill(0, 50, 100);
Sets the fill color to dark blue, which will be used for the text
text("Happy Birthday!", width / 2, cakeY - cakeHeight / 2 - min(width * 0.05, 50));
Draws the text centered horizontally and positioned above the cake; the y-position uses a responsive offset that shrinks on small screens
noStroke();
Turns off the outline/border for all shapes that follow, giving the cake a cleaner appearance
fill(255, 200, 150);
Sets the fill color to light tan/cake color for the main cake body
rect(cakeX - cakeWidth / 2, cakeY - cakeHeight / 2, cakeWidth, cakeHeight, 5);
Draws a rounded rectangle (the '5' argument makes 5-pixel rounded corners) centered at cakeX, cakeY with the calculated cake dimensions
fill(255, 255, 255, 220);
Sets fill to white with 220 alpha (out of 255), making the frosting slightly transparent so it blends beautifully over the cake
arc(cakeX - cakeWidth / 2, cakeY - cakeHeight / 2, cakeWidth / 2, cakeHeight / 4, PI, TWO_PI);
Draws a semi-circular arc at the left edge of the cake from PI to TWO_PI (the bottom half of an ellipse), creating a wavy frosting edge
arc(cakeX + cakeWidth / 2, cakeY - cakeHeight / 2, cakeWidth / 2, cakeHeight / 4, PI, TWO_PI);
Draws a matching arc at the right edge, completing the wavy frosting effect
rect(cakeX - cakeWidth / 4, cakeY - cakeHeight / 2 - cakeHeight / 8, cakeWidth / 2, cakeHeight / 4, 5);
Fills the gap between the two arcs with a white rectangle to complete the frosting layer
let candleSpacing = cakeWidth / (candleCount + 1);
Calculates the distance between candles by dividing cake width by (number of candles + 1), which ensures even spacing with margins on both sides
let candleHeight = cakeHeight / 3;
Sets each candle's height to one-third of the cake height, scaling proportionally as the cake changes size
let candleWidth = 5;
Sets a fixed 5-pixel width for candle sticks, which is thin enough to look like real candles
for (let i = 0; i < candleCount; i++) {
Loops through each candle (0 to candleCount-1), executing the code inside once per candle
let candleX = cakeX - cakeWidth / 2 + (i + 1) * candleSpacing;
Calculates the x-position for each candle by starting at the left edge (cakeX - cakeWidth/2) and adding spacing for the current candle number; (i+1) skips the first spacing gap
let candleY = cakeY - cakeHeight / 2 - candleHeight / 2;
Positions candles at the top of the cake, slightly above it, so they appear to be inserted into the frosting
fill(255, 150, 150);
Sets the fill color to pink for the candle stick
rect(candleX - candleWidth / 2, candleY - candleHeight / 2, candleWidth, candleHeight, 2);
Draws a pink vertical rectangle centered at candleX with slight rounding, creating the candle stick
let flameWidth = candleWidth * 1.5;
Sets the flame's base width to 1.5 times the candle stick width, making flames wider and more visible than the stick
let flameHeight = candleHeight * 0.8;
Sets the flame height to 80% of the candle stick height, creating proportional-looking flames
let flicker = noise(frameCount * 0.1 + i * 100);
Generates a smooth flicker value using Perlin noise; frameCount * 0.1 makes the noise change gradually each frame, and i * 100 offsets each candle's noise so they don't all flicker in sync
let currentFlameWidth = map(flicker, 0, 1, flameWidth * 0.8, flameWidth * 1.2);
Maps the flicker value (0 to 1) to a flame width range (0.8 to 1.2 times the base width), making the flame pulse between narrower and wider
let currentFlameHeight = map(flicker, 0, 1, flameHeight * 0.8, flameHeight * 1.2);
Similarly maps flicker to flame height, creating synchronized width/height pulsing
fill(255, 255, 0, 200);
Sets fill to bright yellow with 200 alpha (slightly transparent) for the flame's bright inner core
ellipse(candleX, candleY - candleHeight / 2 - currentFlameHeight / 4, currentFlameWidth, currentFlameHeight);
Draws a yellow ellipse at the top of the candle stick using the animated flame dimensions, positioned slightly above the stick
fill(255, 165, 0, 150);
Sets fill to orange with 150 alpha (more transparent) for the outer glow layer
ellipse(candleX, candleY - candleHeight / 2 - currentFlameHeight / 4, currentFlameWidth * 1.2, currentFlameHeight * 1.2);
Draws a larger orange ellipse behind the yellow one (drawn second, so it appears on top due to draw order), creating a glowing outer flame effect

windowResized()

windowResized() is a special p5.js function that automatically runs whenever the browser window is resized. By recalculating all the dimensions inside it, we ensure that the cake and text always stay properly proportioned and centered, no matter what screen size. This is responsive design in action—without this function, the sketch would stretch awkwardly on resize.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Recalculate cake properties on resize
  cakeWidth = min(width * 0.6, 400);
  cakeHeight = cakeWidth * 0.6;
  cakeX = width / 2;
  cakeY = height / 2 + cakeHeight / 4;
  textSize(min(width * 0.1, 80));
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

calculation Canvas Resize resizeCanvas(windowWidth, windowHeight);

Stretches or shrinks the canvas to match the new window dimensions when the browser is resized

calculation Cake Dimension Recalculation cakeWidth = min(width * 0.6, 400);

Recalculates all cake properties to maintain proportions and centering on the new canvas size

resizeCanvas(windowWidth, windowHeight);
Updates the canvas size to match the new window dimensions, making the sketch fill the entire screen again
cakeWidth = min(width * 0.6, 400);
Recalculates cake width based on the new window width (60%, capped at 400px)
cakeHeight = cakeWidth * 0.6;
Recalculates cake height to maintain the 60% proportion of width
cakeX = width / 2;
Recenters the cake horizontally on the new canvas width
cakeY = height / 2 + cakeHeight / 4;
Repositions the cake vertically to stay slightly below center on the new canvas height
textSize(min(width * 0.1, 80));
Rescales the text size to fit the new window width (10%, capped at 80px) so the birthday message remains readable

📦 Key Variables

cakeWidth number

Stores the width of the cake in pixels, calculated as a responsive percentage of window width with a maximum cap

let cakeWidth = 300;
cakeHeight number

Stores the height of the cake in pixels, set to 60% of the cake's width to maintain proportions

let cakeHeight = 180;
cakeX number

Stores the horizontal center position of the cake, used as the anchor point for all cake drawings

let cakeX = 400;
cakeY number

Stores the vertical center position of the cake, positioned slightly below the canvas midpoint

let cakeY = 350;
candleCount number

Stores the number of candles to draw on the cake; controls how many flickering flames appear

let candleCount = 5;

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

STYLE draw() — frosting arcs

Drawing two arcs and a rectangle to create frosting is verbose and hard to adjust. The wavy effect would be easier to modify with a single parametric approach.

💡 Consider using a loop with multiple arc positions or a Bezier curve for smoother frosting waves: for(let j = 0; j < 3; j++) { arc(cakeX - cakeWidth/2 + j * cakeWidth/2, ...); }

FEATURE candle flame drawing

The flame effect uses two static ellipses (yellow core + orange glow) which is effective but simple. Real flames have more complexity.

💡 Add a third, semi-transparent red ellipse layer beneath the orange for a more realistic gradient: fill(200, 50, 0, 100); ellipse(candleX, candleY - candleHeight / 2 - currentFlameHeight / 4, currentFlameWidth * 1.4, currentFlameHeight * 1.3);

PERFORMANCE draw() — noise generation

Perlin noise is called once per candle per frame (5+ times per frame), which is fine for small candleCounts but could be optimized.

💡 For many candles (20+), pre-calculate a single noise value per frame and apply offsets to each candle instead of recalculating per iteration: let baseFlicker = noise(frameCount * 0.1); let flicker = (baseFlicker + i * 0.1) % 1;

FEATURE global scope

The sketch doesn't respond to mouse or keyboard input, limiting interactivity.

💡 Add mousePressed() to light/extinguish candles, or use keyPressed() to increment candleCount, making the sketch more engaging

🔄 Code Flow

Code flow showing setup, draw, windowresized

💡 Click on function names in the diagram to jump to their code

graph TD start[Start] --> setup[setup] setup --> canvas-creation[Canvas Creation] setup --> cake-sizing[Cake Sizing] setup --> cake-positioning[Cake Positioning] setup --> draw[draw loop] click setup href "#fn-setup" click canvas-creation href "#sub-canvas-creation" click cake-sizing href "#sub-cake-sizing" click cake-positioning href "#sub-cake-positioning" draw --> background-clear[Background Clear] draw --> text-render[Text Render] draw --> cake-base[Cake Base] draw --> frosting-layer[Frosting Layer] draw --> candle-loop[Candle Loop] click draw href "#fn-draw" click background-clear href "#sub-background-clear" click text-render href "#sub-text-render" click cake-base href "#sub-cake-base" click frosting-layer href "#sub-frosting-layer" click candle-loop href "#sub-candle-loop" candle-loop --> flame-animation[Flame Animation] click flame-animation href "#sub-flame-animation" draw --> draw[draw loop] %% Loop back to draw draw --> windowresized[windowResized] windowresized --> canvas-resize[Canvas Resize] windowresized --> cake-recalculation[Cake Recalculation] click windowresized href "#fn-windowresized" click canvas-resize href "#sub-canvas-resize" click cake-recalculation href "#sub-cake-recalculation"

❓ Frequently Asked Questions

What visual elements are featured in the p5.js birthday cake sketch?

The sketch displays a decorative birthday cake with a 'Happy Birthday!' message, topped with colorful candles arranged symmetrically.

Is there any user interaction in this birthday cake sketch created with p5.js?

The sketch is static and does not include interactive features, focusing solely on rendering a cheerful birthday scene.

What creative coding concepts does this p5.js sketch illustrate?

This sketch demonstrates the use of shapes, colors, and text to create a visually appealing scene, showcasing basic coding techniques in p5.js such as drawing arcs and rectangles.

Preview

Sketch 2026-06-18 13:29 - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of Sketch 2026-06-18 13:29 - Code flow showing setup, draw, windowresized
Code Flow Diagram