AI Laser Harp - Rainbow Light Instrument Play music with light! Move your mouse across the rainbow

46 views · Dec 16, 2025

Create a Laser Harp instrument! Draw vertical laser beams in rainbow colors. When mouse crosses a be

#xelsedia#xelsedai#xelsedp5flashalpha6
🔀 Remix This 📖 View Docs ⛶ Full Screen ← Back to Gallery
📋 Embed Code

Copy this code to embed this sketch on your website:

<iframe src="https://p5js.ai/embed/b625d709-40d8-4bae-9a06-820299a3ea5a" width="400" height="400" frameborder="0" allow="autoplay" title="AI Laser Harp - Rainbow Light Instrument Play music with light! Move your mouse across the rainbow"></iframe>

Adjust width and height as needed. Preview embed →

❓ About This Sketch 3 questions

What visual experience does the AI Laser Harp sketch provide?

The AI Laser Harp sketch creates a vibrant visual display of vertical laser beams in rainbow colors that react as users move their mouse across the screen.

How can users interact with the AI Laser Harp instrument?

Users interact by moving their mouse across the screen, triggering musical notes as they cross the colored laser beams.

What creative coding concepts does the AI Laser Harp showcase?

This sketch demonstrates concepts such as real-time user interaction, sound synthesis with p5.js, and visual mapping of audio frequency to color.

💬 AI Chat History 2 messages
👤 You
Create a Laser Harp instrument! Draw vertical laser beams in rainbow colors. When mouse crosses a beam, it plays a musical note using p5.Oscillator. Show the beam glowing brighter when triggered. Dark background with neon glow effects. Each beam plays a different note of the scale.
🤖 AI
Here’s a complete “Laser Harp” implementation with rainbow beams, neon glow, and p5.Oscillator notes.

Notes:
- Uses [p5.js 1.11.3](https://p5js.org/reference/) and [p5.sound](https://p5js.org/reference/#/libraries/p5.sound).
- Each vertical beam is a different note of a C major scale.
- Move the mouse (or finger on a touchscreen) horizontally across the beams to play.
- Click/tap once to enable sound (required by the browser, via [`userStartAudio`](https://p5js.org/reference/#/p5/userStartAudio)).

---

```html
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Laser Harp</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- p5.js core -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<!-- p5.sound addon (for p5.Oscillator) -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/addons/p5.sound.min.js"></script>
<!-- your sketch -->
<script src="sketch.js"></script>
</body>
</html>
```

```css
/* style.css */
body {
margin: 0;
padding: 0;
overflow: hidden;
background: #02030a; /* dark background behind the canvas */
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

canvas {
display: block;
}
```

```js
// sketch.js

// Number of laser beams
const NUM_BEAMS = 8;

// Simple C major scale (one octave)
const SCALE_FREQS = [
261.63, // C4
293.66, // D4
329.63, // E4
349.23, // F4
392.00, // G4
440.00, // A4
493.88, // B4
523.25 // C5
];

let beams = [];
let audioStarted = false; // flips true after first user interaction

class Beam {
constructor(index, total, freq) {
this.index = index;
this.total = total;
this.freq = freq;

// Hue from 0..360 across beams for rainbow
this.hue = map(index, 0, total - 1, 0, 360);

this.isActive = false; // mouse currently inside this beam
this.lastTriggerTime = -1000; // for glow flash
this.started = false; // has osc.start() been called yet?

// Create oscillator (from p5.sound)
// https://p5js.org/reference/#/p5.Oscillator
this.osc = new p5.Oscillator('triangle'); // nice, slightly harp-like
this.osc.freq(this.freq);
this.osc.amp(0); // start silent
// We will actually start() after user enables audio
}

startOsc() {
if (!this.started) {
this.osc.start();
this.started = true;
}
}

// Current x-position and width depend on canvas size
getX() {
return (this.index + 0.5) * (width / this.total);
}

getW() {
return (width / this.total) * 0.6; // beam width as fraction of slot
}

update(mx, my) {
const x = this.getX();
const w = this.getW();

const inside =
mx >= x - w / 2 &&
mx <= x + w / 2 &&
my >= 0 &&
my <= height;

// Mouse just entered the beam
if (inside && !this.isActive) {
this.isActive = true;
this.trigger();
}
// Mouse just left the beam
else if (!inside && this.isActive) {
this.isActive = false;
this.release();
}
}

trigger() {
this.lastTriggerTime = millis();

if (!audioStarted) return;

this.startOsc();
// Smooth attack to avoid clicks
// osc.amp(amp, rampTime) — see https://p5js.org/reference/#/p5.Oscillator/amp
this.osc.amp(0.7, 0.05);
}

release() {
if (!audioStarted || !this.started) return;
// Smooth release
this.osc.amp(0, 0.2);
}

display() {
const x = this.getX();
const w = this.getW();

// How "bright" should the beam be?
const now = millis();
const timeSinceTrigger = now - this.lastTriggerTime;

// Short flash after triggering
const flash = constrain(1 - timeSinceTrigger / 250.0, 0, 1); // 0..1
const activeBoost = this.isActive ? 1 : 0;

// Overall intensity: max of being active and flash decay
const intensity = max(flash, activeBoost); // 0..1

// Outer glowing beam
push();
rectMode(CENTER);
noStroke();

// Neon glow using canvas shadow
// https://p5js.org/reference/#/p5/drawingContext
drawingContext.shadowColor = color(this.hue, 100, 100, 90);
drawingContext.shadowBlur = 40 + intensity * 40;

fill(this.hue, 80, 60 + intensity * 40, 90);
rect(x, height / 2, w, height * 1.1);
pop();

// Inner bright core
push();
rectMode(CENTER);
noStroke();
fill(0, 0, 100, 60 + intensity * 40); // white core
rect(x, height / 2, w * 0.25, height * 1.1);
pop();
}
}

function setup() {
createCanvas(windowWidth, windowHeight);
colorMode(HSB, 360, 100, 100, 100);
noCursor(); // Let the glowing dot act as our cursor

// Create beams, each with its own oscillator frequency
for (let i = 0; i < NUM_BEAMS; i++) {
const freq = SCALE_FREQS[i % SCALE_FREQS.length];
beams.push(new Beam(i, NUM_BEAMS, freq));
}
}

function draw() {
// Dark background, slightly tinted
background(240, 80, 2); // HSB: dark purplish

// Add a subtle vignette / spotlight
push();
noStroke();
for (let r = max(width, height); r > 0; r -= 40) {
const b = map(r, 0, max(width, height), 20, 0);
fill(240, 80, b, 10);
ellipse(width / 2, height / 2, r * 1.5, r * 1.5);
}
pop();

// Draw beams with additive blend for extra glow
blendMode(ADD);
for (let beam of beams) {
beam.update(mouseX, mouseY);
beam.display();
}
blendMode(BLEND);

// Base bar at the bottom for “instrument” feel
push();
stroke(0, 0, 40, 80);
strokeWeight(3);
line(0, height * 0.9, width, height * 0.9);
pop();

// Glowing cursor dot
push();
noStroke();
fill(180, 80, 100, 80); // bright cyan
ellipse(mouseX, mouseY, 14, 14);
fill(180, 40, 100, 90);
ellipse(mouseX, mouseY, 6, 6);
pop();

// Instructions before audio is enabled
if (!audioStarted) {
push();
textAlign(CENTER, CENTER);
textSize(18);
fill(0, 0, 90);
text(
'Click or tap once to enable sound,\nthen move across the rainbow lasers',
width / 2,
height * 0.15
);
pop();
}
}

// Enable audio on first interaction
// https://p5js.org/reference/#/p5/userStartAudio
function mousePressed() {
if (!audioStarted) {
userStartAudio();
audioStarted = true;
beams.forEach(b => b.startOsc());
}
}

function touchStarted() {
if (!audioStarted) {
userStartAudio();
audioStarted = true;
beams.forEach(b => b.startOsc());
}
// Prevent default scrolling on touch
return false;
}

function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
```

If you’d like, I can next:
- Change the scale (e.g., pentatonic, minor, custom tuning),
- Adjust the envelope so each crossing is a short pluck instead of a sustained tone,
- Or add simple effects like reverb from p5.sound for a more “spacey” harp.
🎬 Video Preview