Totally accurate tops

This sketch simulates a spinning top launching mini-game where a red tracker bounces back and forth across a bar, and you must click to stop it when it lands in the yellow goal zone. The game automatically detects perfect timing and displays results with visual feedback.

🧪 Try This!

Experiment with the code by making these changes:

  1. Speed up the tracker oscillation — Higher trackerSpeed values make the red tracker bounce back and forth faster, making the game harder.
  2. Make the perfect zone wider — Increasing PERFECT_THRESHOLD creates a larger target zone, making it much easier to land a perfect hit.
  3. Change the result message — The Perfect message appears after a successful hit—customize it to anything you want.
  4. Move the goal zone to the right side — Changing goalX repositions the yellow target zone—0 is left, 1 is right, 0.5 is center.
  5. Make any click instant victory — Modifying mousePressed() changes what happens when you click during launch—currently it always gives Perfect, but you can change it.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch recreates a classic top-spinning mechanic from retro video games: a red tracker oscillates across a horizontal bar, and you click to 'launch' when it lands in a yellow target zone. The sketch combines p5.js canvas rendering with HTML DOM elements (divs) to create an interactive interface with instant visual feedback—the goal zone changes color to green for perfect timing or red for a miss.

The code is organized around a central game state machine that tracks whether the game is 'ready', 'launching', 'finished', or reset. You will learn how to coordinate canvas animation with DOM manipulation, implement collision detection between continuous movement and target zones, and create a responsive mini-game loop that resets itself after each result. The sketch also demonstrates event handling with mousePressed() and responsive window resizing.

⚙️ How It Works

  1. When the sketch loads, setup() creates a full-screen canvas, styles a green 'Auto Launch' button, and builds three nested HTML divs: a dark gray launch bar, a yellow goal zone inside it, and a red tracker indicator—all hidden initially.
  2. Clicking the button calls initiateAutoLaunch(), which sets launchState to 'launching', reveals the bar, and hides the button.
  3. Every frame, the draw() function checks if launchState is 'launching' and updates trackerX by adding trackerSpeed, bouncing the tracker off the left and right edges by reversing trackerDirection.
  4. Simultaneously, draw() calculates the absolute distance between the tracker's position and the goal's center, and automatically triggers finish(2) if the tracker lands within PERFECT_THRESHOLD (2.5% of the bar width).
  5. The finish() function halts movement, displays a result message, and changes the goal color—green for perfect, blue for good, or red for miss—then waits 1 second before calling resetGame().
  6. resetGame() restores the initial state, hiding the bar and showing the button again so the player can launch again.

🎓 Concepts You'll Learn

Game state machineDOM element manipulationCollision detectionAnimation loop and oscillationEvent handling (mousePressed)Responsive canvas and layout

📝 Code Breakdown

setup()

setup() runs once when the sketch loads. It initializes the canvas, creates all DOM elements (button, bar, goal, tracker), styles them with colors and positioning, and sets up text rendering. Understanding setup() teaches you how p5.js organizes initialization and how to layer DOM elements with the canvas.

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(220);

  // Auto Launch Button
  // This button initiates the auto-perfect launch sequence
  autoLaunchButton = createButton("Auto Launch (Perfect)");
  autoLaunchButton.position(width / 2 - autoLaunchButton.width / 2, height / 2 - 120);
  autoLaunchButton.mousePressed(initiateAutoLaunch); // Call initiateAutoLaunch on click
  autoLaunchButton.style('padding', '10px 20px');
  autoLaunchButton.style('font-size', '18px');
  autoLaunchButton.style('cursor', 'pointer');
  autoLaunchButton.style('background-color', '#4CAF50'); // Green button
  autoLaunchButton.style('color', 'white');
  autoLaunchButton.style('border', 'none');
  autoLaunchButton.style('border-radius', '5px');

  // Launch Bar Container (p5.Element)
  // This div acts as the background for the tracker and goal
  launchBarDiv = createDiv();
  launchBarDiv.style('width', barWidth + 'px');
  launchBarDiv.style('height', barHeight + 'px');
  launchBarDiv.style('background-color', '#333'); // Dark gray bar
  launchBarDiv.style('border-radius', '5px');
  launchBarDiv.style('position', 'absolute');
  launchBarDiv.position(width / 2 - barWidth / 2, height / 2); // Position using p5.Element.position()
  launchBarDiv.style('overflow', 'hidden'); // Keep tracker and goal inside
  launchBarDiv.style('display', 'none'); // Hidden initially

  // Goal Area (p5.Element, child of launchBarDiv)
  // This div represents the target zone
  goalDiv = createDiv();
  goalDiv.style('width', (goalWidth * barWidth) + 'px');
  goalDiv.style('height', barHeight + 'px');
  goalDiv.style('background-color', '#ffcb00'); // Yellowish color for the goal
  goalDiv.style('border-radius', '5px');
  goalDiv.style('position', 'absolute');
  goalDiv.style('left', ((goalX - goalWidth / 2) * barWidth) + 'px'); // Position relative to parent (launchBarDiv)
  goalDiv.style('top', '0');
  launchBarDiv.child(goalDiv); // Make goal a child of the bar

  // Tracker (p5.Element, child of launchBarDiv)
  // This div represents the moving indicator
  trackerDiv = createDiv();
  trackerDiv.style('width', '10px');
  trackerDiv.style('height', barHeight + 'px');
  trackerDiv.style('background-color', 'red'); // Red tracker
  trackerDiv.style('border-radius', '2px');
  trackerDiv.style('position', 'absolute');
  trackerDiv.style('left', (trackerX * barWidth) + 'px'); // Initial position
  trackerDiv.style('top', '0');
  launchBarDiv.child(trackerDiv); // Make tracker a child of the bar

  // Text styling for results
  textAlign(CENTER, CENTER);
  textSize(24);
  noStroke();
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

initialization Canvas creation createCanvas(windowWidth, windowHeight);

Creates a full-screen canvas that covers the entire browser window

DOM-element Launch button creation autoLaunchButton = createButton("Auto Launch (Perfect)");

Creates a clickable button that triggers the launch sequence

DOM-element Launch bar container launchBarDiv = createDiv();

Creates the dark gray background bar that holds the tracker and goal

DOM-element Goal zone creation goalDiv = createDiv();

Creates the yellow target zone where the tracker should land

DOM-element Tracker creation trackerDiv = createDiv();

Creates the red moving indicator that oscillates across the bar

createCanvas(windowWidth, windowHeight);
Creates a canvas as wide and tall as the browser window, giving p5.js a drawing surface
autoLaunchButton = createButton("Auto Launch (Perfect)");
Creates a clickable HTML button with the text 'Auto Launch (Perfect)' and stores it in the autoLaunchButton variable
autoLaunchButton.position(width / 2 - autoLaunchButton.width / 2, height / 2 - 120);
Centers the button horizontally and places it 120 pixels above the vertical center of the screen
autoLaunchButton.mousePressed(initiateAutoLaunch);
Registers a callback so that clicking the button calls the initiateAutoLaunch() function
launchBarDiv.style('display', 'none');
Hides the launch bar initially—it only shows when the player clicks the launch button
launchBarDiv.child(goalDiv);
Makes the goalDiv a child of launchBarDiv, so the goal moves and stays inside the bar
launchBarDiv.child(trackerDiv);
Makes the trackerDiv a child of launchBarDiv, so the red tracker stays inside the bar and moves with it
textAlign(CENTER, CENTER);
Aligns all text horizontally and vertically centered—result messages ('Perfect!', 'Miss!') will appear in the middle of the screen

draw()

draw() is the game's heartbeat, running 60 times per second. It manages the game state machine by checking launchState and updating the tracker position, detecting collisions, and timing the result display. This function shows you how animation, timing, and collision detection work together in a game loop.

🔬 This is the core bouncing motion—trackerX moves right or left each frame, and bounces off edges. What happens if you change the condition from >= 1 to >= 0.8? Does the tracker bounce earlier, creating a shorter bar?

    trackerX += trackerSpeed * trackerDirection;

    // Reverse direction if tracker hits the bar edges
    if (trackerX >= 1 || trackerX <= 0) {
      trackerDirection *= -1;
      trackerX = constrain(trackerX, 0, 1); // Ensure tracker stays within bounds
    }

🔬 This collision detection fires 'perfect' when the tracker lands in PERFECT_THRESHOLD. What happens if you multiply PERFECT_THRESHOLD by 2 here, making the zone twice as wide? Does it become much easier to hit?

    // Auto-launch logic for "perfect"
    // Check if the tracker is within the perfect zone of the goal
    const absoluteDiff = abs(trackerX - goalX);
    if (absoluteDiff <= PERFECT_THRESHOLD) {
      // Trigger a perfect launch automatically
      finish(2); // 2 means "perfect" based on the Lua code's result codes
    }
function draw() {
  background(220); // Clear background each frame

  // Display launch result if the game is in a 'finished' state
  if (launchState !== "ready" && launchState !== "launching") {
    fill(0); // Black text
    text(launchResultMessage, width / 2, height / 2 - 50);

    // Reset game after a short delay
    if (millis() - launchTime > resultDisplayDuration) {
      resetGame();
    }
  }

  // Update tracker position only if the game is in a 'launching' state
  if (launchState === "launching") {
    trackerX += trackerSpeed * trackerDirection;

    // Reverse direction if tracker hits the bar edges
    if (trackerX >= 1 || trackerX <= 0) {
      trackerDirection *= -1;
      trackerX = constrain(trackerX, 0, 1); // Ensure tracker stays within bounds
    }

    // Update the tracker's DOM position
    trackerDiv.style('left', (trackerX * barWidth) + 'px');

    // Auto-launch logic for "perfect"
    // Check if the tracker is within the perfect zone of the goal
    const absoluteDiff = abs(trackerX - goalX);
    if (absoluteDiff <= PERFECT_THRESHOLD) {
      // Trigger a perfect launch automatically
      finish(2); // 2 means "perfect" based on the Lua code's result codes
    }
  }
}
Line-by-line explanation (14 lines)

🔧 Subcomponents:

initialization Canvas clearing background(220);

Erases the previous frame's drawings with a light gray color, preventing visual trails

conditional Result message display if (launchState !== "ready" && launchState !== "launching") {

Shows the result message ('Perfect!', 'Miss!') only after the launch finishes

conditional Automatic reset timer if (millis() - launchTime > resultDisplayDuration) {

Waits 1 second, then automatically resets the game so the player can launch again

conditional Tracker movement if (launchState === "launching") {

Only updates the tracker's position during the launching phase

conditional Boundary bounce if (trackerX >= 1 || trackerX <= 0) {

Reverses the tracker's direction when it hits the left or right edge of the bar

conditional Collision detection if (absoluteDiff <= PERFECT_THRESHOLD) {

Checks if the tracker has landed in the perfect zone and triggers an automatic finish

background(220);
Fills the entire canvas with light gray (RGB 220,220,220) every frame, erasing the previous frame and preventing motion trails
if (launchState !== "ready" && launchState !== "launching") {
Checks if the game has just finished (launchState is 'finished')—only then should we display the result message
text(launchResultMessage, width / 2, height / 2 - 50);
Draws the result message ('Perfect!', 'Miss!') at the center-top of the screen in black text
if (millis() - launchTime > resultDisplayDuration) {
Checks if 1000 milliseconds (1 second) have passed since the launch finished by comparing the current time (millis()) to the recorded launch time
resetGame();
Resets all variables and game state back to the initial 'ready' state, hiding the bar and showing the button again
if (launchState === "launching") {
Only updates tracker position if the game is actively launching—prevents updates during ready or finished states
trackerX += trackerSpeed * trackerDirection;
Moves the tracker horizontally by adding trackerSpeed (0.005) multiplied by trackerDirection (1 or -1) to trackerX, creating smooth oscillation
if (trackerX >= 1 || trackerX <= 0) {
Detects if the tracker has reached the left edge (0) or right edge (1) of the bar and needs to bounce
trackerDirection *= -1;
Reverses the tracker's direction by multiplying trackerDirection by -1 (if it was 1, it becomes -1; if -1, it becomes 1)
trackerX = constrain(trackerX, 0, 1);
Forces trackerX to stay between 0 and 1, preventing it from ever going beyond the bar's edges after bouncing
trackerDiv.style('left', (trackerX * barWidth) + 'px');
Updates the red tracker div's CSS position on every frame, converting the normalized trackerX value (0-1) into pixel coordinates
const absoluteDiff = abs(trackerX - goalX);
Calculates the distance between the tracker's current position and the goal's center position using the absolute value (always positive)
if (absoluteDiff <= PERFECT_THRESHOLD) {
Checks if the tracker is within 2.5% (PERFECT_THRESHOLD) of the goal's center—if so, it's a perfect hit
finish(2);
Calls the finish() function with result code 2, which ends the launch and displays 'Perfect!' with visual feedback

initiateAutoLaunch()

initiateAutoLaunch() is the transition function that bridges user input (clicking the button) and game logic (launching). It demonstrates the importance of state management—checking the current state before allowing actions, updating the state, and coordinating DOM visibility with game logic.

function initiateAutoLaunch() {
  if (launchState === "ready") {
    launchState = "launching";
    launchBarDiv.style('display', 'block'); // Show the launch bar
    autoLaunchButton.style('display', 'none'); // Hide the button during launch
    trackerX = 0; // Reset tracker position to the start
    trackerDirection = 1; // Start moving right
    launchResultMessage = ""; // Clear previous results
  }
}
Line-by-line explanation (7 lines)

🔧 Subcomponents:

conditional Ready state check if (launchState === "ready") {

Ensures launch only starts if the game is in the 'ready' state, preventing multiple simultaneous launches

assignment State transition launchState = "launching";

Changes the game state to 'launching', which tells draw() to start updating the tracker position

DOM-manipulation Bar visibility launchBarDiv.style('display', 'block');

Reveals the launch bar and tracker that were hidden initially

if (launchState === "ready") {
Checks that the game is in the 'ready' state before allowing launch—prevents clicking the button multiple times
launchState = "launching";
Changes the game state to 'launching', which signals to draw() that tracker updates should happen
launchBarDiv.style('display', 'block');
Makes the launch bar visible by changing its CSS display property from 'none' to 'block'
autoLaunchButton.style('display', 'none');
Hides the launch button during the launching phase so the player can't accidentally click it again
trackerX = 0;
Resets the tracker position to the left edge (0) so it always starts fresh
trackerDirection = 1;
Sets direction to 1 (rightward), ensuring the tracker always moves right initially
launchResultMessage = "";
Clears any previous result message so the screen is clean when the launch starts

finish(resultCode)

finish() is the consequence function—it ends the launch and displays results. It demonstrates switch statements for branching logic, state transitions, and how visual feedback (color changes, messages) responds to game events. This function is where the game reacts to the player's success or failure.

function finish(resultCode) {
  if (launchState !== "launching") return; // Only process if currently launching

  launchState = "finished";
  launchTime = millis(); // Record the time of this launch result

  // Set the result message and update goal color based on the result code
  switch (resultCode) {
    case 2:
      launchResultMessage = "Perfect!";
      goalDiv.style('background-color', '#4CAF50'); // Green for perfect
      break;
    case 1:
      launchResultMessage = "Good!"; // Not used in auto-perfect mode, but kept for completeness
      goalDiv.style('background-color', '#007bff'); // Blue for good
      break;
    case 0:
      launchResultMessage = "Miss!"; // Not used in auto-perfect mode
      goalDiv.style('background-color', '#dc3545'); // Red for miss
      break;
    case 3: // Lua code uses 3 for completing without input (e.g., timeout)
      launchResultMessage = "Time's up! Miss!"; // Not used in auto-perfect mode
      goalDiv.style('background-color', '#dc3545'); // Red for miss
      break;
  }

  // Stop tracker movement
  trackerSpeed = 0;
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

conditional Guard clause if (launchState !== "launching") return;

Exits the function early if the game is not launching, preventing duplicate finishes

assignment State transition to finished launchState = "finished";

Changes game state to 'finished' so draw() knows to display the result message

assignment Timestamp recording launchTime = millis();

Records the current time so draw() can measure when 1 second has passed for auto-reset

switch-case Result evaluation switch (resultCode) {

Routes logic based on the result code (0=Miss, 1=Good, 2=Perfect, 3=Timeout), setting the message and goal color

assignment Halt movement trackerSpeed = 0;

Stops the tracker's oscillation by setting its speed to 0

if (launchState !== "launching") return;
Safety check: if the game is not launching, exit the function immediately, preventing duplicate finish calls
launchState = "finished";
Changes the game state to 'finished' so draw() will display the result message instead of updating the tracker
launchTime = millis();
Captures the current time in milliseconds so draw() can later check if 1 second has passed and auto-reset
switch (resultCode) {
Starts a switch statement that branches into different cases based on the resultCode parameter (0, 1, 2, or 3)
case 2:
Handles result code 2, which represents a 'Perfect' launch
launchResultMessage = "Perfect!";
Sets the message that will be displayed on screen after a perfect hit
goalDiv.style('background-color', '#4CAF50');
Changes the goal zone color to green, providing instant visual feedback for a successful launch
case 1:
Handles result code 1 for a 'Good' launch (defined but not used in auto-perfect mode)
case 0:
Handles result code 0 for a 'Miss' (not used in auto-perfect mode)
trackerSpeed = 0;
Stops the tracker's animation by setting its speed to zero, halting all movement on the bar

resetGame()

resetGame() is the cleanup function that returns the sketch to its initial playable state. It demonstrates how to reverse state changes and reset all visual elements, which is essential for games that loop. After each launch, resetGame() ensures the player can immediately play again without manual intervention.

function resetGame() {
  launchState = "ready";
  launchBarDiv.style('display', 'none'); // Hide the launch bar
  autoLaunchButton.style('display', 'block'); // Show the launch button again
  goalDiv.style('background-color', '#ffcb00'); // Reset goal color
  trackerSpeed = 0.005; // Reset tracker speed
}
Line-by-line explanation (5 lines)

🔧 Subcomponents:

assignment State reset launchState = "ready";

Returns the game to the 'ready' state, allowing the player to launch again

DOM-manipulation Bar visibility reset launchBarDiv.style('display', 'none');

Hides the launch bar and tracker after the result is displayed

DOM-manipulation Button visibility reset autoLaunchButton.style('display', 'block');

Re-displays the launch button so the player can start another round

DOM-manipulation Goal color reset goalDiv.style('background-color', '#ffcb00');

Restores the goal zone to its original yellow color for the next round

assignment Tracker speed reset trackerSpeed = 0.005;

Restores the tracker's normal oscillation speed after it was stopped by finish()

launchState = "ready";
Transitions the game back to 'ready' state, allowing the launch button to work again
launchBarDiv.style('display', 'none');
Hides the launch bar, goal, and tracker by setting their CSS display to 'none'
autoLaunchButton.style('display', 'block');
Re-displays the launch button so the player can initiate another launch
goalDiv.style('background-color', '#ffcb00');
Resets the goal zone color back to its original yellow, removing any green/red result feedback
trackerSpeed = 0.005;
Restores the tracker's movement speed to its default value (it was set to 0 by finish() to stop animation)

windowResized()

windowResized() is an automatic p5.js callback that fires whenever the browser window is resized. It ensures your sketch stays centered and properly scaled no matter the window size. This is crucial for making responsive interactive experiences that work on phones, tablets, and desktops.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  autoLaunchButton.position(width / 2 - autoLaunchButton.width / 2, height / 2 - 120);
  launchBarDiv.position(width / 2 - barWidth / 2, height / 2);
}
Line-by-line explanation (3 lines)

🔧 Subcomponents:

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

Adjusts the p5.js canvas to match the new window size when the browser window is resized

DOM-manipulation Button repositioning autoLaunchButton.position(width / 2 - autoLaunchButton.width / 2, height / 2 - 120);

Re-centers the launch button horizontally and maintains its vertical position when the window size changes

DOM-manipulation Bar repositioning launchBarDiv.position(width / 2 - barWidth / 2, height / 2);

Re-centers the launch bar horizontally when the window is resized

resizeCanvas(windowWidth, windowHeight);
Updates the p5.js canvas dimensions to match the new browser window size
autoLaunchButton.position(width / 2 - autoLaunchButton.width / 2, height / 2 - 120);
Recalculates the button's position to keep it centered horizontally and 120 pixels above vertical center (width and height have changed)
launchBarDiv.position(width / 2 - barWidth / 2, height / 2);
Recalculates the bar's position to keep it centered horizontally and vertically positioned at the screen's middle

mousePressed()

mousePressed() is an automatic p5.js callback that fires whenever the user clicks anywhere on the page. Here it provides an alternative way to end the launch (manual click instead of automatic tracker detection), showing how event listeners can override automatic behavior. Notice it always gives a 'Perfect!' result—you could modify it to check the tracker position and give realistic results instead.

function mousePressed() {
  if (launchState === "launching") {
    finish(2); // Trigger a perfect launch (result code 2)
  }
}
Line-by-line explanation (2 lines)

🔧 Subcomponents:

conditional Launch state check if (launchState === "launching") {

Only allows finishing if the game is actively launching—prevents accidental clicks outside launch windows

function-call Perfect finish trigger finish(2);

Calls finish() with result code 2, immediately ending the launch with a 'Perfect!' result

if (launchState === "launching") {
Checks if the game is currently launching—if the game is 'ready' or 'finished', clicks are ignored
finish(2);
Calls the finish() function with result code 2 (Perfect), ending the launch immediately and displaying 'Perfect!' regardless of tracker position

📦 Key Variables

launchBarDiv p5.Element (div)

Stores the dark gray launch bar container that holds the tracker and goal zone

let launchBarDiv;
trackerDiv p5.Element (div)

Stores the red moving tracker indicator that oscillates across the bar

let trackerDiv;
goalDiv p5.Element (div)

Stores the yellow target zone where the tracker should land for a perfect hit

let goalDiv;
autoLaunchButton p5.Element (button)

Stores the green button that the player clicks to start a launch sequence

let autoLaunchButton;
barWidth number

Width of the launch bar in pixels (600px)—used for positioning and calculations

let barWidth = 600;
barHeight number

Height of the launch bar in pixels (40px)—used to style the tracker and goal zone

let barHeight = 40;
trackerX number

Current horizontal position of the tracker, normalized to 0–1 (0=left edge, 1=right edge)

let trackerX = 0;
trackerSpeed number

How fast the tracker moves left/right each frame (as a fraction per frame)

let trackerSpeed = 0.005;
trackerDirection number

Direction of tracker movement: 1 for rightward, -1 for leftward

let trackerDirection = 1;
goalX number

Horizontal center position of the goal zone, normalized to 0–1 (0.5 = center of bar)

let goalX = 0.5;
goalWidth number

Width of the goal zone as a fraction of bar width (0.2 = 20% of bar width)

let goalWidth = 0.2;
launchState string

Tracks the current game phase: 'ready', 'launching', or 'finished'—controls which logic executes

let launchState = "ready";
launchResultMessage string

Stores the result message ('Perfect!', 'Miss!', etc.) to display on screen after launch finishes

let launchResultMessage = "";
launchTime number

Timestamp (in milliseconds) when the launch finished, used to time the auto-reset delay

let launchTime = 0;
resultDisplayDuration number (constant)

How long the result message stays on screen before auto-reset (1000 milliseconds = 1 second)

const resultDisplayDuration = 1000;
PERFECT_THRESHOLD number (constant)

The maximum distance the tracker can be from the goal center to register a perfect hit (0.025 = 2.5% of bar width)

const PERFECT_THRESHOLD = 0.025;

🔧 Potential Improvements (5)

Here are some ways this code could be enhanced:

FEATURE mousePressed()

Mouse clicks always trigger a 'Perfect' result (finish(2)) regardless of actual tracker position, bypassing the collision detection entirely

💡 Modify mousePressed() to call finish() with a result based on the actual distance between trackerX and goalX. Use abs(trackerX - goalX) to check if it's within PERFECT_THRESHOLD (perfect), GOOD_THRESHOLD (good), or outside (miss).

BUG draw() collision detection loop

The auto-finish triggers every frame once the tracker enters the perfect zone, potentially firing finish() multiple times if the tracker lingers there

💡 Add a flag (e.g., let hasFinished = false) that is set to true the first time finish() is called during a launch, preventing it from triggering multiple times per round.

STYLE finish() switch statement

Cases 1, 0, and 3 are defined but never actually used because auto-perfect mode always calls finish(2), making the code confusing

💡 Remove unused cases or add comments explaining they are placeholders for future game modes (e.g., 'May be used for manual mode'). Alternatively, implement those modes to give meaningful results for different accuracy levels.

PERFORMANCE draw()

trackerDiv.style('left', ...) is recalculated every frame even though the tracker position might not have changed visually (sub-pixel updates)

💡 Add a variable to track the last rendered pixel position (e.g., lastRenderedPixel) and only update the DOM if the pixel position actually changes, reducing unnecessary DOM writes.

FEATURE setup() and draw()

No audio feedback (sounds for perfect, miss, etc.) exists, making the experience less satisfying

💡 Use p5.sound or the Web Audio API to play a satisfying 'ding' sound on perfect hits and a buzz sound on misses, adding auditory feedback to match the visual feedback.

🔄 Code Flow

Code flow showing setup, draw, initiateautolaunch, finish, resetgame, windowresized, mousepressed

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

graph TD start[Start] --> setup[setup] setup --> create-canvas[create-canvas] setup --> create-button[create-button] setup --> create-bar-div[create-bar-div] setup --> create-goal-div[create-goal-div] setup --> create-tracker-div[create-tracker-div] setup --> draw[draw loop] click setup href "#fn-setup" click create-canvas href "#sub-create-canvas" click create-button href "#sub-create-button" click create-bar-div href "#sub-create-bar-div" click create-goal-div href "#sub-create-goal-div" click create-tracker-div href "#sub-create-tracker-div" draw --> clear-background[clear-background] draw --> state-check[state-check] draw --> update-tracker[update-tracker] draw --> bounce-logic[bounce-logic] draw --> perfect-detection[perfect-detection] draw --> display-result[display-result] draw --> auto-reset[auto-reset] draw --> guard-clause[guard-clause] draw --> state-transition[state-transition] click draw href "#fn-draw" click clear-background href "#sub-clear-background" click state-check href "#sub-state-check" click update-tracker href "#sub-update-tracker" click bounce-logic href "#sub-bounce-logic" click perfect-detection href "#sub-perfect-detection" click display-result href "#sub-display-result" click auto-reset href "#sub-auto-reset" click guard-clause href "#sub-guard-clause" click state-transition href "#sub-state-transition" initiateautolaunch[initiateAutoLaunch] --> state-check initiateautolaunch --> state-transition initiateautolaunch --> show-bar[show-bar] click initiateautolaunch href "#fn-initiateautolaunch" click show-bar href "#sub-show-bar" finish[finish] --> state-transition-finished[state-transition-finished] finish --> result-switch[result-switch] finish --> stop-tracker[stop-tracker] finish --> hide-bar[hide-bar] finish --> show-button[show-button] finish --> reset-color[reset-color] click finish href "#fn-finish" click state-transition-finished href "#sub-state-transition-finished" click result-switch href "#sub-result-switch" click stop-tracker href "#sub-stop-tracker" click hide-bar href "#sub-hide-bar" click show-button href "#sub-show-button" click reset-color href "#sub-reset-color" resetgame[resetGame] --> reset-state[reset-state] resetgame --> reset-speed[reset-speed] click resetgame href "#fn-resetgame" click reset-state href "#sub-reset-state" click reset-speed href "#sub-reset-speed" windowresized[windowResized] --> canvas-resize[canvas-resize] windowresized --> button-reposition[button-reposition] windowresized --> bar-reposition[bar-reposition] click windowresized href "#fn-windowresized" click canvas-resize href "#sub-canvas-resize" click button-reposition href "#sub-button-reposition" click bar-reposition href "#sub-bar-reposition" mousepressed[mousePressed] --> launch-state-check[launch-state-check] mousepressed --> perfect-finish[perfect-finish] click mousepressed href "#fn-mousepressed" click launch-state-check href "#sub-launch-state-check" click perfect-finish href "#sub-perfect-finish"

❓ Frequently Asked Questions

What visual elements are featured in the Totally Accurate Tops sketch?

The sketch creates a dynamic launch bar where a tracker moves horizontally, aiming to align with a goal indicator, providing a visually engaging representation of accuracy in launching.

How can users participate in the Totally Accurate Tops interactive coding sketch?

Users can interact by clicking the 'Auto Launch (Perfect)' button to initiate an automatic launch sequence, demonstrating the sketch's functionality.

What creative coding concepts does the Totally Accurate Tops sketch illustrate?

This sketch demonstrates concepts of user interaction, timing, and accuracy thresholds, showcasing how creative coding can simulate a game-like experience.

Preview

Totally accurate tops - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of Totally accurate tops - Code flow showing setup, draw, initiateautolaunch, finish, resetgame, windowresized, mousepressed
Code Flow Diagram