Lofi-Radio / visualizer.js
Ivan000's picture
Upload 5 files
27630e2 verified
export class Visualizer {
constructor(audioContext, canvasId) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.analyser = audioContext.createAnalyser();
// Ensure fftSize is a valid power of two (16384 is the max standard value)
this.analyser.fftSize = 16384;
this.bufferLength = this.analyser.frequencyBinCount;
this.dataArray = new Uint8Array(this.bufferLength);
this.sampleRate = audioContext.sampleRate;
this.binWidth = this.sampleRate / this.analyser.fftSize;
// Adjusted frequency range
this.startBin = Math.floor(20 / this.binWidth);
this.endBin = Math.floor(200 / this.binWidth);
this.usableBins = this.endBin - this.startBin;
// Slightly reduced smoothing for faster response
this.analyser.smoothingTimeConstant = 0.45; // Reduced from 0.5
// Peak tracking with slightly more dynamic range
this.peakLevels = new Array(this.usableBins).fill(0);
this.peakDecayRate = 0.85; // Slightly faster decay
this.dynamicPeakMultiplier = 1.25; // Increased from 1.2
this.resize();
window.addEventListener('resize', () => this.resize());
}
resize() {
const dpr = window.devicePixelRatio || 1;
this.canvas.width = this.canvas.offsetWidth * dpr;
this.canvas.height = this.canvas.offsetHeight * dpr;
this.ctx.scale(dpr, dpr);
this.canvas.style.width = this.canvas.offsetWidth + 'px';
this.canvas.style.height = this.canvas.offsetHeight + 'px';
}
getAverageAmplitude() {
let sum = 0;
for (let i = this.startBin; i < this.endBin; i++) {
sum += this.dataArray[i];
}
// Slightly increased sensitivity
return Math.min((sum / this.usableBins / 255) * 0.75, 1);
}
draw() {
this.analyser.getByteFrequencyData(this.dataArray);
const width = this.canvas.offsetWidth;
const height = this.canvas.offsetHeight;
// Slightly more transparent fade effect with a color closer to the background
this.ctx.fillStyle = 'rgba(31, 3, 107, 0.3)';
this.ctx.fillRect(0, 0, width, height);
const barWidth = (width / this.usableBins) * 0.9;
const spacing = barWidth * 0.1;
let x = (width - (this.usableBins * (barWidth + spacing))) / 2;
for (let i = this.startBin; i < this.endBin; i++) {
const index = i - this.startBin;
const currentValue = this.dataArray[i];
// More controlled peak tracking
this.peakLevels[index] = Math.max(
currentValue,
Math.min(
this.peakLevels[index] * this.peakDecayRate,
currentValue * this.dynamicPeakMultiplier
)
);
// Slightly reduced peak height scaling
const barHeight = Math.min(
(this.peakLevels[index] * 0.9) * (height / 255),
height
);
const gradient = this.ctx.createLinearGradient(0, height, 0, height - barHeight);
gradient.addColorStop(0, 'rgba(106, 74, 194, 0.8)'); // Matching the end color of background
gradient.addColorStop(1, 'rgba(31, 3, 107, 0.4)'); // Matching the start color of background
this.ctx.fillStyle = gradient;
this.ctx.beginPath();
this.ctx.roundRect(
x,
height - barHeight,
barWidth,
barHeight,
3
);
this.ctx.fill();
x += barWidth + spacing;
}
return this.getAverageAmplitude();
}
}