
๐ p5.js
let audioCtx;
let activeNotes = new Map();
let visualEvents = [];
const keyMap = {
a: 261.63,
s: 293.66,
d: 329.63,
f: 349.23,
g: 392.0,
h: 440.0,
j: 493.88,
};
function setup() {
createCanvas(800, 800);
background(20);
textAlign(CENTER, CENTER);
textSize(14);
}
function draw() {
background(20, 40);
for (let i = visualEvents.length - 1; i >= 0; i--) {
let v = visualEvents[i];
v.radius += 2;
v.alpha -= 4;
noFill();
stroke(255, v.alpha);
circle(width / 2, height / 2, v.radius);
if (v.alpha <= 0) {
visualEvents.splice(i, 1);
}
}
fill(200);
noStroke();
text("A S D F G H J ํค๋ฅผ ๋๋ฌ ์ฐ์ฃผํฉ๋๋ค", width / 2, height - 40);
}
function keyPressed() {
let k = key.toLowerCase();
if (!(k in keyMap)) return;
if (!audioCtx) {
audioCtx = new AudioContext();
}
if (activeNotes.has(k)) return;
let freq = keyMap[k];
let osc = audioCtx.createOscillator();
let gain = audioCtx.createGain();
osc.type = "sine";
osc.frequency.value = freq;
let now = audioCtx.currentTime;
gain.gain.setValueAtTime(0, now);
gain.gain.linearRampToValueAtTime(0.5, now + 0.05);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
activeNotes.set(k, { osc, gain });
visualEvents.push({
radius: 10,
alpha: 255,
});
}
function keyReleased() {
let k = key.toLowerCase();
if (!activeNotes.has(k)) return;
let { osc, gain } = activeNotes.get(k);
let now = audioCtx.currentTime;
gain.gain.cancelScheduledValues(now);
gain.gain.setValueAtTime(gain.gain.value, now);
gain.gain.linearRampToValueAtTime(0, now + 0.2);
osc.stop(now + 0.25);
activeNotes.delete(k);
}