horrible game I think it works this time by corbun

This sketch creates an interactive liquid chrome shader effect that responds to mouse movement, generating a shimmering, mercury-like surface that ripples and distorts in real-time. The visual effect is accompanied by generative techno music that intensifies based on how fast you move your mouse across the canvas.

🧪 Try This!

Experiment with the code by making these changes:

  1. Change the synth waveform to sine for a smooth tone — A sine wave sounds pure and smooth compared to the harsh sawtooth—great for mellow techno vibes.
  2. Increase maximum synth frequency to 800 Hz for higher pitches — The synth will reach shriller, higher pitches when you move the mouse fast—creates a more piercing effect.
  3. Double the maximum synth volume to 0.4 — The synth will be louder overall—more aggressive and in-your-face when you move the mouse rapidly.
  4. Make ripples settle slower by changing 0.95 to 0.98 — Velocity will decay more slowly, so ripple effects will linger longer and create sustained visual trails after you stop moving.
  5. Trigger glitch effects at lower speeds (20 instead of 30) — Random frequency jumps will start appearing even at slow mouse movements, making the synth glitchier overall.
  6. Reduce maximum distortion to 0.2 for a subtler effect — The synth will sound less harsh and gritty—more refined even at high velocities.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates a mesmerizing liquid chrome effect that fills your entire screen and reacts to your mouse movements. The visual is powered by a custom GLSL fragment shader that calculates ripple patterns, distortions, and reflections based on the mouse position and velocity. As you move your cursor, the effect intensifies and morphs, creating a hypnotic, metallic liquid surface. A generative techno synth layer adds audio feedback—the faster you move, the higher the pitch, the more distortion, and the more intense the delay echoes become.

The code is organized into three main pieces: preload() loads the custom GLSL shader files and a web font, setup() initializes the p5.sound audio objects (synth, reverb, delay, and distortion), and draw() runs every frame to calculate mouse velocity, map that velocity to audio parameters and shader uniforms, then render the effect. By studying this sketch you will learn how to load and use custom GLSL shaders in p5.js, pass real-time data to shaders via uniforms, generate sound with oscillators and effects, and create complex audio-visual feedback loops.

⚙️ How It Works

  1. When the sketch loads, preload() fetches two GLSL shader files (vertex.glsl and fragment.glsl) and loads a web font, while setup() creates a WEBGL canvas and initializes a sawtooth wave synthesizer with reverb, delay, and distortion effects chained to it.
  2. Every frame, draw() calculates how far and fast your mouse moved since the last frame by subtracting the previous mouse position from the current position, then stores the current position for the next calculation.
  3. The velocity magnitude (total speed) is mapped to audio parameters: higher velocity triggers higher synth frequencies (50–600 Hz), louder amplitude (0–0.2 volume), random frequency jumps for glitchiness, and increased distortion (0–0.5 amount).
  4. The delay feedback is also modulated by velocity—faster movement creates more echo feedback and randomness, making the synth sound chaotic and reactive.
  5. The velocity magnitude and mouse position are passed to the GLSL fragment shader as uniforms, along with the canvas resolution and elapsed time in seconds.
  6. The shader renders a full-screen rectangle with liquid chrome calculations, creating ripples, distortions, and reflections that respond to the uniforms in real-time.
  7. A 'made by corbun' text label is drawn in the bottom-left corner, and the canvas automatically resizes when the window resizes.

🎓 Concepts You'll Learn

GLSL Fragment ShadersShader UniformsWEBGL RenderingMouse Velocity CalculationP5.sound SynthesisAudio Effects (Reverb, Delay, Distortion)Audio-Visual FeedbackReal-time Parameter ModulationCanvas ResizingOscillators and Waveforms

📝 Code Breakdown

preload()

preload() runs once before setup() and is the only place where loadShader() and loadFont() should be called in p5.js. These are slow operations, so they must happen upfront before the sketch starts drawing.

function preload() {
  // Load the custom shaders
  // Ensure 'vertex.glsl' and 'fragment.glsl' files are present and contain GLSL code
  myShader = loadShader('vertex.glsl', 'fragment.glsl');
  
  // Load a font for text in WEBGL mode
  // Using Fontsource CDN for reliable WEBGL-compatible fonts
  myFont = loadFont('https://unpkg.com/@fontsource/roboto@latest/files/roboto-latin-400-normal.woff');

  // Initialize velocity variables for the first frame
  prevMouseX = mouseX;
  prevMouseY = mouseY;
  velocityX = 0;
  velocityY = 0;
  velocityMagnitude = 0;
}
Line-by-line explanation (7 lines)

🔧 Subcomponents:

function-call GLSL Shader Loading myShader = loadShader('vertex.glsl', 'fragment.glsl');

Loads two GLSL files that define how vertices and fragments (pixels) are rendered—the heart of the visual effect

function-call Web Font Loading myFont = loadFont('https://unpkg.com/@fontsource/roboto@latest/files/roboto-latin-400-normal.woff');

Loads a web-friendly font file so text displays correctly in WEBGL mode

initialization Initial Velocity Setup prevMouseX = mouseX;

Stores the mouse position at startup so the first frame's velocity calculation has valid previous coordinates

myShader = loadShader('vertex.glsl', 'fragment.glsl');
Loads two GLSL shader files from your project folder. The vertex shader processes geometry, the fragment shader calculates pixel colors. These files must exist alongside sketch.js.
myFont = loadFont('https://unpkg.com/@fontsource/roboto@latest/files/roboto-latin-400-normal.woff');
Loads a web font from a CDN (online source) because WEBGL mode requires specially formatted fonts—regular fonts won't work in WEBGL.
prevMouseX = mouseX;
Saves the current mouse X position so on the first draw() frame, we can calculate velocity by comparing previous position to current position.
prevMouseY = mouseY;
Saves the current mouse Y position for the same reason as prevMouseX.
velocityX = 0;
Initializes horizontal velocity to zero—before any mouse movement, there is no motion.
velocityY = 0;
Initializes vertical velocity to zero for the same reason.
velocityMagnitude = 0;
Initializes the total speed (magnitude) to zero—this value will be recalculated every frame based on how far the mouse moved.

setup()

setup() runs once after preload() to initialize the canvas and audio system. The p5.sound effects must be chained in order (synth → reverb → delay → distortion) so each effect processes the previous one. All audio must start with userStartAudio() to comply with modern browser audio policies.

function setup() {
  createCanvas(windowWidth, windowHeight, WEBGL);
  noStroke(); // No p5.js strokes, shader handles all rendering

  // Set the loaded font and text size for the "made by corbun" text
  textFont(myFont);
  textSize(16); // Increased text size
  fill(255); // White text color

  // Initialize p5.sound components for techno music
  // Using a sawtooth wave for a typical techno synth sound
  technoSynth = new p5.Oscillator('sawtooth');
  technoSynth.amp(0); // Start silent
  technoSynth.freq(100); // Base frequency
  technoSynth.start();

  // Add reverb for a spacious, metallic sound
  technoReverb = new p5.Reverb();
  technoReverb.process(technoSynth, 3, 2); // 3 seconds decay, 2% dry/wet
  technoReverb.amp(0.1); // Subtle reverb volume

  // Add delay for a rhythmic echo effect
  technoDelay = new p5.Delay();
  technoDelay.process(technoSynth, 0.25, 0.5, 2300); // 0.25 delay time, 0.5 feedback, 2300 ms filter frequency
  technoDelay.amp(0.1); // Subtle delay volume

  // Add a distortion effect for the glitchy sound
  technoDistortion = new p5.Distortion(0.0); // Start with no distortion
  technoDistortion.process(technoSynth);
  technoDistortion.amp(0.1); // Subtle distortion volume

  // Start the Web Audio context. This is required for audio to play in modern browsers.
  userStartAudio();
}
Line-by-line explanation (16 lines)

🔧 Subcomponents:

function-call WEBGL Canvas Setup createCanvas(windowWidth, windowHeight, WEBGL);

Creates a full-screen WEBGL canvas that stretches to fill the browser window—WEBGL enables GPU-accelerated shader rendering

initialization Synthesizer Initialization technoSynth = new p5.Oscillator('sawtooth');

Creates a sawtooth wave oscillator, which has a harsh, buzzy tone perfect for techno music

initialization Reverb Effect Chain technoReverb.process(technoSynth, 3, 2);

Chains reverb effect to the synth to add spacious, echoing resonance with 3-second decay

initialization Delay Effect Chain technoDelay.process(technoSynth, 0.25, 0.5, 2300);

Chains delay effect to create rhythmic echoes that repeat every 250 milliseconds

initialization Distortion Effect Chain technoDistortion.process(technoSynth);

Chains distortion effect to add grit and harshness when velocity is high

function-call Web Audio Context Start userStartAudio();

Initializes the browser's Web Audio API context, which is required by modern browsers before sound can play

createCanvas(windowWidth, windowHeight, WEBGL);
Creates a canvas that fills the entire browser window using WEBGL mode, which enables custom shader rendering on the GPU.
noStroke();
Disables p5.js outline strokes because the shader will handle all visual rendering—no traditional p5.js shapes are needed.
textFont(myFont);
Sets the font loaded in preload() as the active font for all future text() calls.
textSize(16);
Sets the text size to 16 pixels for the 'made by corbun' label.
fill(255);
Sets text color to white (255 = maximum brightness in RGB).
technoSynth = new p5.Oscillator('sawtooth');
Creates a sawtooth oscillator—a waveform that sounds metallic and bright, perfect for techno music.
technoSynth.amp(0);
Starts the synth at zero volume (silent) so it doesn't make noise until the mouse moves.
technoSynth.freq(100);
Sets the starting frequency to 100 Hz, a moderately low note that will be modulated by mouse velocity.
technoSynth.start();
Starts the oscillator running—without this, no sound will ever play.
technoReverb = new p5.Reverb();
Creates a reverb effect object that will add spacious, echoing room reflections to the synth.
technoReverb.process(technoSynth, 3, 2);
Connects reverb to the synth with 3 seconds of decay time and 2% dry/wet mix (mostly dry, subtle effect).
technoDelay = new p5.Delay();
Creates a delay effect object that will repeat the synth sound at regular intervals.
technoDelay.process(technoSynth, 0.25, 0.5, 2300);
Connects delay to the synth: 0.25 second delay time (250 ms), 0.5 feedback (each echo is half as loud), 2300 Hz filter.
technoDistortion = new p5.Distortion(0.0);
Creates a distortion effect starting at 0 (no distortion), which will increase as the mouse moves faster.
technoDistortion.process(technoSynth);
Connects distortion to the synth so it can be modulated during draw().
userStartAudio();
Initializes the browser's Web Audio API context—required by modern browsers for any sound to play.

draw()

draw() is the heart of this sketch—it runs every frame (60 times per second in most browsers) to calculate velocity, modulate audio parameters, pass data to the shader as uniforms, and render the effect. Uniforms are variables the CPU sends to the GPU-side shader; they allow the shader to respond to interactive input and time-based changes. The rect() call is just a geometry placeholder—the shader overwrites all its pixels with the liquid chrome effect.

🔬 These two if-statements add random frequency jumps at different speed thresholds. What happens if you change the conditions to > 20 and > 40 instead? The synth will glitch at lower speeds. Try it and move your mouse slowly.

  if (velocityMagnitude > 30) {
    freq += random(-20, 20); // Add a small random offset
  }
  if (velocityMagnitude > 60) {
    freq += random(-50, 50); // Add a larger random offset
  }

🔬 The amp variable controls how loud the synth gets. What happens if you change the 0.2 to 0.4 or 0.05? Higher values make the synth louder at peak velocity, lower values keep it quieter. Move fast after changing it.

  let amp = map(velocityMagnitude, 0, 100, 0, 0.2); // Map velocity (0-100) to amplitude (0-0.2 volume)

  technoSynth.freq(freq);
  technoSynth.amp(amp, 0.1);
function draw() {
  // Calculate mouse velocity for this frame
  // Multiply by 0.5 to make the effect less aggressive and allow for smoother transitions
  velocityX = (mouseX - prevMouseX) * 0.5;
  velocityY = (mouseY - prevMouseY) * 0.5;

  // Store current mouse position for the next frame's velocity calculation
  prevMouseX = mouseX;
  prevMouseY = mouseY;

  // Smoothly decay velocity over time, making ripples settle
  // This gives the "liquid mercury" feel where movement creates ripples that fade
  velocityX *= 0.95;
  velocityY *= 0.95;

  // Calculate the magnitude (length) of the velocity vector
  velocityMagnitude = sqrt(velocityX * velocityX + velocityY * velocityY);

  // Modulate techno music based on velocityMagnitude
  // Map velocityMagnitude to frequency and amplitude of the synth
  // This makes the music more intense and glitchy as you move the mouse faster

  // Base frequency mapping
  let baseFreq = map(velocityMagnitude, 0, 100, 50, 400); // Map velocity (0-100) to frequency (50-400 Hz)
  let freq = baseFreq;

  // Add random frequency jumps for glitchiness when velocity is high
  if (velocityMagnitude > 30) {
    freq += random(-20, 20); // Add a small random offset
  }
  if (velocityMagnitude > 60) {
    freq += random(-50, 50); // Add a larger random offset
  }
  freq = constrain(freq, 50, 600); // Keep frequency within a reasonable range

  let amp = map(velocityMagnitude, 0, 100, 0, 0.2); // Map velocity (0-100) to amplitude (0-0.2 volume)

  technoSynth.freq(freq);
  technoSynth.amp(amp, 0.1); // Smooth amplitude change over 0.1 seconds

  // Modulate distortion amount based on velocityMagnitude
  // Faster movement = more distortion
  let distortionAmount = map(velocityMagnitude, 0, 100, 0, 0.5); // Map velocity to distortion amount (0-0.5)
  technoDistortion.amount.value = distortionAmount;

  // Randomize delay feedback slightly for glitchiness when velocity is high
  if (velocityMagnitude > 40) {
    let delayFeedback = map(velocityMagnitude, 0, 100, 0.5, 0.7); // Base feedback
    delayFeedback += random(-0.05, 0.05); // Add random variation
    delayFeedback = constrain(delayFeedback, 0.4, 0.8); // Keep feedback within a reasonable range
    technoDelay.feedback.value = delayFeedback;
  } else {
    technoDelay.feedback.value = 0.5; // Default feedback when calm
  }

  // Set the custom shader to be used for rendering
  shader(myShader);

  // Pass uniforms (variables) to the fragment shader
  myShader.setUniform('u_resolution', [width, height]); // Canvas resolution
  myShader.setUniform('u_mouse', [mouseX, mouseY]);     // Mouse position
  myShader.setUniform('u_velocityMagnitude', velocityMagnitude); // Mouse velocity magnitude
  myShader.setUniform('u_time', millis() / 1000.0);      // Time in seconds

  // Draw a rectangle that covers the entire canvas.
  // The shader will then render the liquid chrome effect on this rectangle.
  rect(0, 0, width, height);

  // Draw "made by corbun" text
  // In WEBGL, (0,0) is the center. To place in bottom-left, use negative width/2 and positive height/2.
  text("made by corbun", -width/2 + 10, height/2 - 10);
}
Line-by-line explanation (33 lines)

🔧 Subcomponents:

calculation Mouse Velocity Calculation velocityX = (mouseX - prevMouseX) * 0.5;

Subtracts the previous frame's mouse position from the current position to get movement distance, then dampens it by 0.5x

assignment Store Current Position prevMouseX = mouseX;

Saves the current mouse position so the next frame can calculate new velocity relative to this point

calculation Velocity Damping velocityX *= 0.95;

Multiplies velocity by 0.95 every frame (5% loss per frame) to gradually reduce ripple intensity

calculation Velocity Magnitude velocityMagnitude = sqrt(velocityX * velocityX + velocityY * velocityY);

Calculates total speed (Pythagorean theorem) to use as a single number for audio and shader modulation

calculation Frequency Modulation let baseFreq = map(velocityMagnitude, 0, 100, 50, 400);

Maps velocity (0-100 range) to synth frequency (50-400 Hz) so fast movement triggers high pitches

conditional Low-Speed Glitch Trigger if (velocityMagnitude > 30) {

When velocity exceeds 30, add small random frequency jumps (±20 Hz) for glitchiness

conditional High-Speed Glitch Trigger if (velocityMagnitude > 60) {

When velocity exceeds 60, add larger random frequency jumps (±50 Hz) for more extreme glitchiness

calculation Amplitude Modulation let amp = map(velocityMagnitude, 0, 100, 0, 0.2);

Maps velocity to volume (0-0.2) so the synth gets louder as the mouse moves faster

calculation Distortion Modulation let distortionAmount = map(velocityMagnitude, 0, 100, 0, 0.5);

Maps velocity to distortion amount (0-0.5) so fast movement creates harsh, gritty sounds

conditional Fast-Movement Delay Feedback if (velocityMagnitude > 40) {

When velocity exceeds 40, increase and randomize delay feedback to create chaotic echo patterns

function-call Shader Uniform Passing myShader.setUniform('u_velocityMagnitude', velocityMagnitude);

Sends velocity magnitude to the fragment shader so it can create ripples and distortions based on movement

function-call Shader Rendering rect(0, 0, width, height);

Draws a full-screen rectangle that the shader paints with the liquid chrome effect using the passed uniforms

velocityX = (mouseX - prevMouseX) * 0.5;
Calculates how far the mouse moved horizontally this frame (current position minus previous position), then dampens it by multiplying by 0.5 to smooth transitions.
velocityY = (mouseY - prevMouseY) * 0.5;
Calculates how far the mouse moved vertically this frame, dampened by 0.5.
prevMouseX = mouseX;
Saves the current mouse X position so the next frame can calculate velocity relative to this position.
prevMouseY = mouseY;
Saves the current mouse Y position for the same reason.
velocityX *= 0.95;
Reduces velocity by 5% every frame (multiply by 0.95) so the effect gradually fades after the mouse stops moving—this creates the ripple-settling effect.
velocityY *= 0.95;
Reduces vertical velocity by 5% every frame.
velocityMagnitude = sqrt(velocityX * velocityX + velocityY * velocityY);
Calculates the total speed using the Pythagorean theorem: √(vx² + vy²). This gives a single number representing how fast the mouse is moving in any direction.
let baseFreq = map(velocityMagnitude, 0, 100, 50, 400);
Uses map() to convert velocity (0–100 range) into a synth frequency (50–400 Hz): 0 velocity = 50 Hz (low note), 100 velocity = 400 Hz (high note).
let freq = baseFreq;
Copies baseFreq to freq, which will be modified with random glitch offsets before being sent to the synth.
if (velocityMagnitude > 30) {
Starts a conditional block that runs when velocity exceeds 30—a moderate movement speed threshold for glitch effects.
freq += random(-20, 20);
Adds a random value between -20 and +20 Hz to freq, creating small, unpredictable pitch jumps (glitchiness) at moderate speeds.
if (velocityMagnitude > 60) {
Starts another conditional block that runs when velocity exceeds 60—a high movement speed threshold.
freq += random(-50, 50);
Adds larger random pitch jumps (±50 Hz) at high speeds, making the effect more chaotic and extreme.
freq = constrain(freq, 50, 600);
Clamps freq to stay between 50 and 600 Hz—prevents the frequency from going too extreme and sounding broken.
let amp = map(velocityMagnitude, 0, 100, 0, 0.2);
Maps velocity (0–100) to synth volume (0–0.2): no movement = silent, high velocity = loud.
technoSynth.freq(freq);
Sets the synth's frequency to the calculated freq value (base frequency + glitch offsets + constraints).
technoSynth.amp(amp, 0.1);
Sets the synth's amplitude to amp, smoothly ramping the volume change over 0.1 seconds (prevents clicks and pops).
let distortionAmount = map(velocityMagnitude, 0, 100, 0, 0.5);
Maps velocity (0–100) to distortion (0–0.5): no movement = no distortion, high velocity = heavy distortion/grittiness.
technoDistortion.amount.value = distortionAmount;
Sets the distortion effect's amount to the calculated value, making the synth sound harsher as you move faster.
if (velocityMagnitude > 40) {
Starts a conditional block that runs when velocity exceeds 40—a threshold for dramatic delay feedback changes.
let delayFeedback = map(velocityMagnitude, 0, 100, 0.5, 0.7);
Maps velocity (0–100) to delay feedback (0.5–0.7): at 40 velocity, feedback is 0.5 (each echo is half the volume), at 100 velocity, feedback is 0.7 (echoes persist longer).
delayFeedback += random(-0.05, 0.05);
Adds a small random variation (±0.05) to the delay feedback, creating unpredictable echo patterns at high speeds.
delayFeedback = constrain(delayFeedback, 0.4, 0.8);
Clamps delayFeedback between 0.4 and 0.8 to keep it stable (prevents runaway feedback).
technoDelay.feedback.value = delayFeedback;
Sets the delay effect's feedback amount, controlling how long the echoes ring.
} else {
This else block runs when velocity is 40 or below (calm movement).
technoDelay.feedback.value = 0.5;
Resets delay feedback to 0.5 (neutral) when the mouse is moving slowly—keeps the effect stable at rest.
shader(myShader);
Activates the custom GLSL shader for all subsequent drawing operations.
myShader.setUniform('u_resolution', [width, height]);
Passes the canvas width and height to the shader so it can calculate pixel coordinates and aspect ratio.
myShader.setUniform('u_mouse', [mouseX, mouseY]);
Passes the current mouse X and Y coordinates to the shader so it can create ripples and distortions around the cursor.
myShader.setUniform('u_velocityMagnitude', velocityMagnitude);
Passes the calculated velocity magnitude to the shader so it can intensify effects based on how fast the mouse is moving.
myShader.setUniform('u_time', millis() / 1000.0);
Passes elapsed time in seconds (millis() / 1000) to the shader so it can animate ripples, waves, and other time-based patterns.
rect(0, 0, width, height);
Draws a rectangle covering the entire canvas (positioned at origin 0,0 with dimensions width×height). The shader paints the liquid chrome effect on this rectangle's pixels.
text("made by corbun", -width/2 + 10, height/2 - 10);
Draws the text 'made by corbun' in WEBGL mode (where 0,0 is the center), positioned 10 pixels from the left edge and 10 pixels from the bottom edge.

windowResized()

windowResized() is a special p5.js function that automatically runs whenever the browser window is resized. It's necessary here because the shader needs to know the canvas dimensions to calculate pixel coordinates correctly. Without this function, resizing the window would break the visual effect.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Update the resolution uniform in the shader when the canvas is resized
  myShader.setUniform('u_resolution', [width, height]);
}
Line-by-line explanation (2 lines)

🔧 Subcomponents:

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

Resizes the p5.js canvas to match the browser window's new dimensions when the user resizes their window

function-call Shader Uniform Update myShader.setUniform('u_resolution', [width, height]);

Updates the shader's u_resolution uniform so it knows the canvas size and can calculate pixels correctly at the new resolution

resizeCanvas(windowWidth, windowHeight);
Automatically resizes the p5.js canvas when the browser window is resized—otherwise the sketch would stay at the old canvas size and letterbox.
myShader.setUniform('u_resolution', [width, height]);
After resizing the canvas, immediately updates the shader's u_resolution uniform with the new dimensions so the shader's calculations stay correct.

📦 Key Variables

myShader p5.Shader

Holds the loaded GLSL shader program (vertex + fragment shaders) that renders the liquid chrome effect

let myShader; // Populated in preload()
prevMouseX number

Stores the mouse's X position from the previous frame, used to calculate horizontal velocity

let prevMouseX = 0;
prevMouseY number

Stores the mouse's Y position from the previous frame, used to calculate vertical velocity

let prevMouseY = 0;
velocityX number

The horizontal component of mouse velocity (pixels per frame), decays over time to create ripple settling

let velocityX = 0;
velocityY number

The vertical component of mouse velocity (pixels per frame), decays over time

let velocityY = 0;
velocityMagnitude number

The total speed (Pythagorean magnitude of velocityX and velocityY), used to modulate audio and shader effects

let velocityMagnitude = 0;
technoSynth p5.Oscillator

The sawtooth wave synthesizer that generates the techno sounds, modulated by velocity

let technoSynth; // Created in setup()
technoReverb p5.Reverb

A reverb effect that adds spacious, echoing room reflections to the synth

let technoReverb; // Created and processed in setup()
technoDelay p5.Delay

A delay effect that repeats the synth sound at 250 ms intervals with feedback, modulated by velocity

let technoDelay; // Created and processed in setup()
technoDistortion p5.Distortion

A distortion effect that adds harshness and grittiness to the synth, increasing with velocity

let technoDistortion; // Created and processed in setup()
myFont p5.Font

Holds the loaded web font (Roboto) for rendering text in WEBGL mode

let myFont; // Loaded from CDN in preload()

🔧 Potential Improvements (5)

Here are some ways this code could be enhanced:

PERFORMANCE draw() → audio modulation section

Every frame, random() is called multiple times in the glitch conditionals (4 random calls per frame minimum), which is computationally expensive and causes unnecessary CPU work.

💡 Cache random values or use Perlin noise (noise()) to generate smoother, less frequent random variations. Alternatively, only update glitch values every 3-5 frames using frameCount % 5 to reduce call frequency.

BUG draw() → delay feedback modulation

The delay feedback is only updated when velocityMagnitude > 40; if velocity drops below 40, feedback stays at its previous high value (up to 0.7) until recalculated, creating lingering echoes even at rest.

💡 Always update delay feedback every frame, not just in the if-block. Move 'technoDelay.feedback.value = 0.5;' outside the else block or set it before the condition so it updates every frame.

STYLE preload() and setup()

Audio effects are created in setup() but their parameters (amplitude, feedback, distortion) are not documented in comments explaining their initial values and typical ranges.

💡 Add inline comments explaining typical ranges: e.g., 'Reverb decay typically 2-6 seconds', 'Delay feedback 0.4-0.8 prevents runaway', 'Distortion 0-1.0 range'. Helps future readers understand audio design choices.

FEATURE draw() → shader uniforms

The sketch passes mouse position and velocity to the shader but does not expose mouse button state or keyboard input, limiting interactive control over audio or visual parameters.

💡 Pass mousePressed or key states as additional uniforms (e.g., u_mousePressed, u_keyDown) to give users more control—e.g., hold mouse button to lock frequency, press keys to toggle effects.

BUG setup() → audio context initialization

userStartAudio() is called but not guarded by a check—in some browsers/devices, calling it repeatedly can cause warnings or failures if the audio context is already started.

💡 Check if the audio context is already running before calling: e.g., if (!getAudioContext().isRunning) { userStartAudio(); }

🔄 Code Flow

Code flow showing preload, setup, draw, windowresized

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

graph TD start[Start] --> preload[preload] preload --> setup[setup] click preload href "#fn-preload" click setup href "#fn-setup" setup --> canvas-creation[canvas-creation] setup --> audio-context[audio-context] setup --> synth-init[synth-init] setup --> reverb-init[reverb-init] setup --> delay-init[delay-init] setup --> distortion-init[distortion-init] click canvas-creation href "#sub-canvas-creation" click audio-context href "#sub-audio-context" click synth-init href "#sub-synth-init" click reverb-init href "#sub-reverb-init" click delay-init href "#sub-delay-init" click distortion-init href "#sub-distortion-init" setup --> velocity-init[velocity-init] click velocity-init href "#sub-velocity-init" draw[draw loop] --> velocity-calc[velocity-calc] click draw href "#fn-draw" click velocity-calc href "#sub-velocity-calc" velocity-calc --> position-storage[position-storage] click position-storage href "#sub-position-storage" position-storage --> velocity-decay[velocity-decay] click velocity-decay href "#sub-velocity-decay" velocity-decay --> magnitude-calc[magnitude-calc] click magnitude-calc href "#sub-magnitude-calc" magnitude-calc --> freq-mapping[freq-mapping] click freq-mapping href "#sub-freq-mapping" freq-mapping --> glitch-low[glitch-low] click glitch-low href "#sub-glitch-low" glitch-low --> glitch-high[glitch-high] click glitch-high href "#sub-glitch-high" freq-mapping --> amp-mapping[amp-mapping] click amp-mapping href "#sub-amp-mapping" amp-mapping --> distortion-mod[distortion-mod] click distortion-mod href "#sub-distortion-mod" velocity-decay --> delay-feedback-fast[delay-feedback-fast] click delay-feedback-fast href "#sub-delay-feedback-fast" draw --> shader-uniform-pass[shader-uniform-pass] click shader-uniform-pass href "#sub-shader-uniform-pass" shader-uniform-pass --> shader-rendering[shader-rendering] click shader-rendering href "#sub-shader-rendering" draw --> windowresized[windowresized] click windowresized href "#fn-windowresized" windowresized --> canvas-resize[canvas-resize] click canvas-resize href "#sub-canvas-resize" canvas-resize --> uniform-update[uniform-update] click uniform-update href "#sub-uniform-update"

❓ Frequently Asked Questions

What visual effects does the 'horrible game I think it works this time' sketch create?

This sketch utilizes custom shaders for dynamic visual effects while rendering in WEBGL, resulting in an immersive and potentially abstract visual experience.

How can users interact with the sketch created by corbun?

Users can interact with the sketch by moving their mouse, which influences the visual elements and potentially alters the sound generated by the techno synth.

What creative coding concepts are showcased in this p5.js sketch?

The sketch demonstrates the integration of audio synthesis with visual shaders, showcasing the use of p5.sound for music generation alongside real-time visual manipulation.

Preview

horrible game I think it works this time by corbun - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of horrible game I think it works this time by corbun - Code flow showing preload, setup, draw, windowresized
Code Flow Diagram