YouTube

This sketch creates a playful video player interface that displays random YouTube video titles in a large thumbnail box. Clicking the X button in the bottom-left corner skips to the next video with a beep sound and increments a counter.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make the beep higher or lower — Lower frequencies sound like deep boops, higher frequencies like alarm beeps—hear the difference instantly.
  2. Make the button bigger — Increase the button size percentage so it becomes easier to click and dominates the interface.
  3. Change the background to dark mode — Swap the light gray background for a dark color so the sketch feels like a modern dark-themed video player.
  4. Move the button to the top-right — Reposition the skip button to the top-right corner, making the layout feel fresh and different.
  5. Add more videos to skip through — Add your own video titles to the videoTitles array so there are more options to discover.
  6. Make the hover effect change color instead of highlight — When hovering over the button, fill it with red instead of drawing a red outline—a more dramatic visual feedback.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch recreates the essence of a YouTube video player—a large thumbnail displaying video titles that change when you click a skip button. It teaches interaction design by combining p5.js techniques like responsive canvas sizing, mouse collision detection, and sound synthesis through the p5.sound library. The sketch also demonstrates how to structure a simple state machine, where clicking advances through an array of data and triggers both visual and audio feedback.

The code is organized into setup() for initialization, draw() for continuous rendering and layout calculations, helper functions for collision detection and interaction, and a mouseClicked() handler for button responses. By studying it, you will learn how to build interactive UI elements, use p5.Oscillator to create sound effects, make sketches scale beautifully across different screen sizes, and structure state that responds to user input.

⚙️ How It Works

  1. When the sketch loads, setup() initializes the canvas to fill the window, creates a sine wave oscillator (ready for sound), and sets text properties for displaying titles.
  2. Every frame, draw() calculates responsive sizes for the thumbnail and button based on window dimensions, then renders the video thumbnail box with the current title, the white X button with a red hover highlight, and the skip counter in the top-right.
  3. The isHoveringXButton() function checks whether the mouse is within the button's circular area using distance calculation.
  4. When you click the mouse, mouseClicked() tests if the click was inside the button; if yes, it triggers a brief sound beep by ramping the oscillator's amplitude up and down.
  5. After the sound plays, the code advances to the next video in the array using modulo arithmetic (wrapping back to the first video after the last), increments the skip counter, and optionally displays an alert for special videos.
  6. windowResized() keeps the canvas and all elements proportional when the browser window is resized.

🎓 Concepts You'll Learn

Mouse interaction and collision detectionSound synthesis with oscillatorsResponsive design and scalingState management with arraysModulo arithmetic for cyclingConditional rendering on hover

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. It's where you initialize your canvas size, prepare sound objects, and set default drawing properties. Using windowWidth and windowHeight makes sketches responsive to any screen size.

function setup() {
  createCanvas(windowWidth, windowHeight);
  // Tip: Use windowWidth/windowHeight for responsive canvas

  // Initialize the sound effect
  clickSound = new p5.Oscillator('sine'); // A simple sine wave
  clickSound.freq(440); // A-note frequency
  clickSound.amp(0);   // Start with 0 amplitude
  clickSound.start();

  // Set text properties
  textSize(24);
  textAlign(CENTER, CENTER);
  fill(0); // Black text
}
Line-by-line explanation (7 lines)

🔧 Subcomponents:

function-call Canvas initialization createCanvas(windowWidth, windowHeight);

Creates a canvas that fills the entire browser window, enabling responsive layout

object-creation Sound oscillator clickSound = new p5.Oscillator('sine');

Creates a sound generator using a sine wave, ready to play beep sounds

function-call Text styling textAlign(CENTER, CENTER);

Sets all text drawn later to be centered both horizontally and vertically

createCanvas(windowWidth, windowHeight);
Creates a canvas matching the full browser window size, so the sketch uses all available space
clickSound = new p5.Oscillator('sine');
Creates a sound oscillator using a smooth sine wave—the building block for our beep sound
clickSound.freq(440);
Sets the oscillator to play at 440 Hz, which is the musical note A4—a pleasant tone for UI feedback
clickSound.amp(0);
Starts with amplitude 0 (silent) so the sound is ready but not playing until we click the button
clickSound.start();
Starts the oscillator running continuously in the background—we control its volume to trigger the beep
textSize(24);
Sets a default text size (though draw() will override this with responsive sizing)
textAlign(CENTER, CENTER);
All text will be drawn centered around its x,y coordinate instead of starting from top-left

draw()

draw() runs 60 times per second, constantly redrawing the interface. By calculating positions based on window size, the sketch stays beautiful on any device. The hover highlight teaches responsive UI design—visual feedback that shows users where they can click.

🔬 These lines control the video box size as a percentage of the canvas. What happens if you change 0.8 to 0.5 and 0.6 to 0.4? How does the thumbnail shrink?

  let thumbnailWidth = width * 0.8;
  let thumbnailHeight = height * 0.6;

🔬 These two lines draw the X on the button. What if you change them to draw a circle instead? Try using arc() or ellipse() in place of the lines.

  line(xButtonX - lineLength / 2, xButtonY - lineLength / 2, xButtonX + lineLength / 2, xButtonY + lineLength / 2);
  line(xButtonX + lineLength / 2, xButtonY - lineLength / 2, xButtonX - lineLength / 2, xButtonY + lineLength / 2);
function draw() {
  background(220); // Light gray background

  // Calculate button size and position based on canvas size
  xButtonSize = min(width, height) * 0.08; // 8% of the smaller dimension
  xButtonX = xButtonMargin + xButtonSize / 2;
  xButtonY = height - xButtonMargin - xButtonSize / 2;

  // --- Draw the "video thumbnail" (represented by text) ---
  let thumbnailWidth = width * 0.8;
  let thumbnailHeight = height * 0.6;
  let thumbnailX = (width - thumbnailWidth) / 2;
  let thumbnailY = (height - thumbnailHeight) / 2;

  // Draw a placeholder rectangle for the video
  fill(200); // Darker gray for the thumbnail background
  noStroke();
  rect(thumbnailX, thumbnailY, thumbnailWidth, thumbnailHeight, 10); // Rounded corners

  // Draw the video title
  fill(0); // Black text
  noStroke();
  textSize(min(width, height) * 0.04); // Responsive text size
  text(videoTitles[currentVideoIndex], thumbnailX + thumbnailWidth / 2, thumbnailY + thumbnailHeight / 2);

  // --- Draw the "X" button ---
  fill(255); // White circle background
  stroke(0); // Black border
  strokeWeight(2);
  ellipse(xButtonX, xButtonY, xButtonSize);

  // Draw the "X" lines
  stroke(0); // Black lines
  strokeWeight(3);
  let lineLength = xButtonSize * 0.6;
  line(xButtonX - lineLength / 2, xButtonY - lineLength / 2, xButtonX + lineLength / 2, xButtonY + lineLength / 2);
  line(xButtonX + lineLength / 2, xButtonY - lineLength / 2, xButtonX - lineLength / 2, xButtonY + lineLength / 2);

  // --- Draw the "Videos Skipped" counter ---
  textSize(min(width, height) * 0.03);
  fill(0);
  noStroke();
  textAlign(RIGHT, TOP);
  text(`Videos Skipped: ${videosSkipped}`, width - xButtonMargin, xButtonMargin);

  // Highlight button on hover/touch
  if (isHoveringXButton()) {
    noFill();
    stroke(255, 0, 0); // Red border on hover
    strokeWeight(4);
    ellipse(xButtonX, xButtonY, xButtonSize + 5); // Slightly larger highlight
  }
}
Line-by-line explanation (14 lines)

🔧 Subcomponents:

calculation Button size and position calculation xButtonSize = min(width, height) * 0.08;

Makes the button scale responsively—8% of the smaller screen dimension ensures it fits well on any device

function-call Draw video thumbnail box rect(thumbnailX, thumbnailY, thumbnailWidth, thumbnailHeight, 10);

Renders the main gray rectangle that represents a video thumbnail, with rounded corners

function-call Display video title text text(videoTitles[currentVideoIndex], thumbnailX + thumbnailWidth / 2, thumbnailY + thumbnailHeight / 2);

Draws the current video title from the array at the center of the thumbnail

function-call Draw X button circle ellipse(xButtonX, xButtonY, xButtonSize);

Draws the white circular background for the skip button

function-call Draw X symbol lines line(xButtonX - lineLength / 2, xButtonY - lineLength / 2, xButtonX + lineLength / 2, xButtonY + lineLength / 2);

Draws the two diagonal lines that form the X symbol on the button

conditional Hover state highlight if (isHoveringXButton()) {

Detects when the mouse is over the button and draws a red outline to show interactivity

background(220);
Clears the canvas with light gray, erasing the previous frame so new content draws fresh each frame
xButtonSize = min(width, height) * 0.08;
Calculates button size as 8% of the smaller screen dimension, ensuring it scales proportionally on any device
xButtonX = xButtonMargin + xButtonSize / 2;
Positions the button 20 pixels from the left edge, offset by half the button's size so the center lands correctly
xButtonY = height - xButtonMargin - xButtonSize / 2;
Positions the button 20 pixels up from the bottom, using the same half-size offset for proper centering
let thumbnailWidth = width * 0.8;
Makes the video box 80% of the screen width, leaving margins on the sides
let thumbnailHeight = height * 0.6;
Makes the video box 60% of the screen height, leaving space for the button below
rect(thumbnailX, thumbnailY, thumbnailWidth, thumbnailHeight, 10);
Draws a rounded rectangle (the 10 is corner radius) in medium gray to represent the video thumbnail
textSize(min(width, height) * 0.04);
Sets text size to 4% of the smaller dimension, so titles stay readable on all screen sizes
text(videoTitles[currentVideoIndex], thumbnailX + thumbnailWidth / 2, thumbnailY + thumbnailHeight / 2);
Draws the current video title from the array centered inside the thumbnail box
ellipse(xButtonX, xButtonY, xButtonSize);
Draws a white circle for the button background at the calculated position and size
line(xButtonX - lineLength / 2, xButtonY - lineLength / 2, xButtonX + lineLength / 2, xButtonY + lineLength / 2);
Draws the first diagonal line of the X, from top-left to bottom-right of the button
line(xButtonX + lineLength / 2, xButtonY - lineLength / 2, xButtonX - lineLength / 2, xButtonY + lineLength / 2);
Draws the second diagonal line of the X, from top-right to bottom-left, completing the X symbol
if (isHoveringXButton()) {
Checks whether the mouse is currently inside the button's circular area
ellipse(xButtonX, xButtonY, xButtonSize + 5);
When hovering, draws a red outline slightly larger than the button to show it is interactive

isHoveringXButton()

This collision detection function uses p5.js's dist() to measure how far the mouse is from the button's center. If that distance is less than the radius, the mouse is inside the circle. This is the foundation of clickable UI design.

🔬 This function uses distance-based collision detection. What happens if you change the comparison from < to > ? Try it and click around—the button's behavior inverts!

function isHoveringXButton() {
  let d = dist(mouseX, mouseY, xButtonX, xButtonY);
  return d < xButtonSize / 2;
}
// Check if mouse/touch is over the "X" button
function isHoveringXButton() {
  let d = dist(mouseX, mouseY, xButtonX, xButtonY);
  return d < xButtonSize / 2;
}
Line-by-line explanation (2 lines)

🔧 Subcomponents:

calculation Distance calculation let d = dist(mouseX, mouseY, xButtonX, xButtonY);

Computes the distance from the mouse to the button center using p5.js's dist() function

conditional Radius comparison return d < xButtonSize / 2;

Returns true if the distance is less than the button's radius, meaning the mouse is inside

let d = dist(mouseX, mouseY, xButtonX, xButtonY);
Calculates the distance in pixels from the current mouse position to the center of the button using the Pythagorean theorem
return d < xButtonSize / 2;
Returns true if the distance is smaller than the button's radius (half its size), meaning the mouse is inside the circular button

mouseClicked()

mouseClicked() is p5.js's event handler that runs when the user clicks or taps. By checking isHoveringXButton() first, we ensure the button only responds when clicked. The sound effect uses amplitude ramping to create a natural attack and decay, mimicking real audio envelopes.

🔬 The modulo operator (%) makes the index wrap around. What happens if you remove the % videoTitles.length part and just use (currentVideoIndex + 1)? Try clicking many times—will you eventually get an error?

    // Move to the next video
    currentVideoIndex = (currentVideoIndex + 1) % videoTitles.length;
    videosSkipped++;
// Handle mouse clicks (also works for touch on p5.js)
function mouseClicked() {
  if (isHoveringXButton()) {
    // Play a short click sound
    clickSound.amp(0.5, 0.05); // Ramp up to 0.5 amplitude over 0.05 seconds
    clickSound.amp(0, 0.2, 0.1); // Ramp down to 0 amplitude over 0.2 seconds after 0.1 second delay

    // Move to the next video
    currentVideoIndex = (currentVideoIndex + 1) % videoTitles.length;
    videosSkipped++;

    // Optional: display special messages for specific videos
    if (videoTitles[currentVideoIndex] === "Me at the zoo") {
      alert("You've found the FIRST ever YouTube video!");
    } else if (videoTitles[currentVideoIndex] === "Oblivion guards being Oblivion guards for 20 minutes") {
      alert("You've found the LATEST ever (for this game!) YouTube video!");
    }
  }
}
Line-by-line explanation (7 lines)

🔧 Subcomponents:

conditional Button click detection if (isHoveringXButton()) {

Checks if the mouse click occurred inside the button before responding

function-call Beep sound playback clickSound.amp(0.5, 0.05);

Starts the beep by ramping the sound amplitude up quickly, creating an audible click

function-call Beep decay clickSound.amp(0, 0.2, 0.1);

Fades out the beep by ramping the amplitude back to zero, making it sound like a short tone

calculation Cycle to next video currentVideoIndex = (currentVideoIndex + 1) % videoTitles.length;

Advances to the next video in the array, wrapping back to 0 after the last video using modulo

calculation Increment skip counter videosSkipped++;

Increases the skip counter by 1 each time the button is clicked

conditional Special video detection if (videoTitles[currentVideoIndex] === "Me at the zoo") {

Detects specific videos and displays celebratory messages to reward exploration

if (isHoveringXButton()) {
Checks if the mouse click happened inside the button area—only then should we respond
clickSound.amp(0.5, 0.05);
Ramps the oscillator's volume up to 0.5 over 0.05 seconds, creating the attack (start) of the beep
clickSound.amp(0, 0.2, 0.1);
After a 0.1-second delay, ramps the volume back down to 0 over 0.2 seconds, creating the release (end) of the beep
currentVideoIndex = (currentVideoIndex + 1) % videoTitles.length;
Moves to the next video in the array; the % (modulo) operator wraps back to index 0 after the last video
videosSkipped++;
Increments the skip counter, so the display shows how many videos have been skipped
if (videoTitles[currentVideoIndex] === "Me at the zoo") {
Checks if the new video title matches the famous first YouTube video
alert("You've found the FIRST ever YouTube video!");
Displays a celebratory pop-up message when the player lands on this special video

windowResized()

windowResized() is called automatically by p5.js whenever the browser window changes size. By resizing the canvas immediately, the sketch stays beautiful and responsive. All the calculations in draw() use width and height, so the entire layout scales automatically.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Called when preview panel is resized
}
Line-by-line explanation (1 lines)

🔧 Subcomponents:

function-call Canvas resize resizeCanvas(windowWidth, windowHeight);

Adjusts the canvas size to match the new window dimensions when the browser is resized

resizeCanvas(windowWidth, windowHeight);
Updates the canvas to fill the entire browser window whenever it is resized, keeping the sketch responsive

📦 Key Variables

videoTitles array

Stores all the video titles that display in rotation. Each click cycles through this list.

const videoTitles = ["Me at the zoo", "ASMR Rain Sounds...", ...];
currentVideoIndex number

Tracks which video in the videoTitles array is currently displayed (0 = first video, 1 = second, etc.)

let currentVideoIndex = 0;
videosSkipped number

Counts how many times the skip button has been clicked, displayed in the top-right corner

let videosSkipped = 0;
xButtonSize number

Stores the calculated size of the skip button, which scales based on window dimensions

xButtonSize = min(width, height) * 0.08;
xButtonMargin number

Controls the distance in pixels the button sits from the bottom-left corner of the canvas

let xButtonMargin = 20;
xButtonX number

Stores the calculated horizontal (x) position of the button's center

xButtonX = xButtonMargin + xButtonSize / 2;
xButtonY number

Stores the calculated vertical (y) position of the button's center

xButtonY = height - xButtonMargin - xButtonSize / 2;
clickSound object

A p5.Oscillator that generates the beep sound when the button is clicked

clickSound = new p5.Oscillator('sine');

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

BUG mouseClicked() function

The function has no return statement. p5.js expects event handlers to return false to prevent default browser behavior, which can cause unwanted side effects like text selection or scrolling.

💡 Add 'return false;' at the end of mouseClicked() to suppress default browser behavior when clicking inside the canvas.

PERFORMANCE draw() function

The oscillator is created in setup() and left running continuously. If the sketch runs for hours, the oscillator thread stays active even when silent, consuming resources.

💡 Consider stopping the oscillator when not needed, or creating a more efficient beep using p5.sound's built-in playSound methods instead of manual amplitude ramping.

STYLE mouseClicked() and draw() functions

Magic numbers like 0.08, 0.6, and 0.04 are scattered throughout without explanation, making it hard to tweak the design proportionally.

💡 Define these as named constants at the top of the sketch (e.g., const BUTTON_SIZE_PERCENT = 0.08;) so the design is easy to adjust in one place.

FEATURE sketch overall

Clicking the skip button cycles through the entire array in order. Players cannot easily revisit favorite videos or see all titles without clicking 20+ times.

💡 Add a shuffle option, search feature, or favorite-marking system so players can jump directly to videos they enjoyed, making the player more interactive.

🔄 Code Flow

Code flow showing setup, draw, ishovering xbutton, mouseclicked, windowresized

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

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] click setup href "#fn-setup" click draw href "#fn-draw" draw --> canvas-creation[canvas-creation] draw --> oscillator-setup[oscillator-setup] draw --> text-defaults[text-defaults] draw --> button-positioning[button-positioning] draw --> thumbnail-drawing[thumbnail-drawing] draw --> title-display[title-display] draw --> x-button-circle[x-button-circle] draw --> x-button-lines[x-button-lines] draw --> hover-highlight[hover-highlight] button-positioning -->|Calculate size & position| button-size[Button Size Calculation] button-positioning -->|Calculate size & position| button-position[Button Position Calculation] hover-highlight --> distance-calc[distance-calc] distance-calc --> radius-check[radius-check] radius-check --> hover-check[hover-check] hover-check -->|If true| sound-trigger[sound-trigger] sound-trigger --> sound-decay[sound-decay] sound-decay --> advance-video[advance-video] advance-video --> increment-counter[increment-counter] increment-counter --> special-video-alert[special-video-alert] window --> windowresized[windowresized] windowresized --> canvas-resize[canvas-resize] canvas-resize --> draw click canvas-creation href "#sub-canvas-creation" click oscillator-setup href "#sub-oscillator-setup" click text-defaults href "#sub-text-defaults" click button-positioning href "#sub-button-positioning" click thumbnail-drawing href "#sub-thumbnail-drawing" click title-display href "#sub-title-display" click x-button-circle href "#sub-x-button-circle" click x-button-lines href "#sub-x-button-lines" click hover-highlight href "#sub-hover-highlight" click distance-calc href "#sub-distance-calc" click radius-check href "#sub-radius-check" click hover-check href "#sub-hover-check" click sound-trigger href "#sub-sound-trigger" click sound-decay href "#sub-sound-decay" click advance-video href "#sub-advance-video" click increment-counter href "#sub-increment-counter" click special-video-alert href "#sub-special-video-alert" click canvas-resize href "#sub-canvas-resize"

❓ Frequently Asked Questions

What visual elements does the YouTube sketch display?

The sketch features a large gray rectangle representing a video thumbnail, along with a rotating list of playful video titles displayed in bold text.

How can users interact with the YouTube sketch?

Users can click the bold X button in the bottom-left corner to skip to the next random video title, accompanied by a short beep sound.

What creative coding concepts does this sketch illustrate?

This sketch demonstrates the use of arrays to manage dynamic content and simple sound integration for interactive feedback.

Preview

YouTube - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of YouTube - Code flow showing setup, draw, ishovering xbutton, mouseclicked, windowresized
Code Flow Diagram