I have JavaScript code that draws vertical bars and changes their color based on the average music frequency. I tried to change the color of each bar to match the color of the previous bar, but I got a different color.
const fileInput = document.getElementById('fileInput');
const canvas = document.getElementById('canvas');
const canvasCtx = canvas.getContext('2d');
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// Function to resize canvas
function resizeCanvas() {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas(); // Initial call to set canvas size
let barColors = [];
const defaultBarColors = [];
fileInput.addEventListener('change', function() {
const file = this.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
audioCtx.decodeAudioData(e.target.result, function(buffer) {
visualize(buffer);
});
};
reader.readAsArrayBuffer(file);
}
});
let rev;
function visualize(buffer) {
const source = audioCtx.createBufferSource();
source.buffer = buffer;
const analyser = audioCtx.createAnalyser();
source.connect(analyser);
analyser.connect(audioCtx.destination);
analyser.fftSize = 512;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
source.start();
draw();
function draw() {
requestAnimationFrame(draw);
analyser.getByteFrequencyData(dataArray);
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
const barWidth = Math.floor(canvas.width / 5);
const sensitivity = 1;
const frequencyBands = [
{ from: 0, to: Math.floor(bufferLength * 0.2) }, // Low frequencies
{ from: Math.floor(bufferLength * 0.2), to: Math.floor(bufferLength * 0.4) }, // Low-mid frequencies
{ from: Math.floor(bufferLength * 0.4), to: Math.floor(bufferLength * 0.6) }, // Mid frequencies
{ from: Math.floor(bufferLength * 0.6), to: Math.floor(bufferLength * 0.8) }, // Upper-mid frequencies
{ from: Math.floor(bufferLength * 0.8), to: bufferLength } // High frequencies
];
let x = 0;
for (let i = 0; i < frequencyBands.length; i++) { // 0,1,2,3,4
const { from, to } = frequencyBands[i];
const sum = dataArray.slice(from, to).reduce((acc, val) => acc + val, 0);
const avg = sum / (to - from);
let colorValue = rev[i];
if (avg > 0) {
if (i > 0) {
colorValue = rev[i - 1];
} else if (i == 0) {
colorValue = "rgb(255, 255, 255)"; // Если первая колонка, то белый цвет
}
} else {
colorValue = rev[i];
}
canvasCtx.fillStyle = colorValue;
const barHeight = Math.max((avg / 255) * canvas.height * sensitivity, canvas.height);
canvasCtx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
x += barWidth;
}
}
}
function initializeCanvas() {
const barWidth = Math.floor(canvas.width / 5);
let x = 0;
for (let i = 4; i >= 0; i--) {
const colorValue = Math.floor(((i / 5) * (240 - 51)) + 51);
const color = `rgb(${colorValue}, ${colorValue}, ${colorValue})`;
defaultBarColors[i] = color;
canvasCtx.fillStyle = color;
canvasCtx.fillRect(x, 0, barWidth, canvas.height);
x += barWidth;
}
rev = defaultBarColors.reverse();
}
initializeCanvas();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#canvas {
width: 100%;
height: 100vh;
display: block;
}
</style>
</head>
<body>
<input type="file" id="fileInput" accept="audio/*">
<canvas id="canvas"></canvas>
</body>
</html>
The main part where color is setted for each bar here:
let colorValue = rev[i];
if (avg > 0) {
if (i > 0) {
colorValue = rev[i - 1];
} else if (i == 0) {
colorValue = "rgb(255, 255, 255)"; // Если первая колонка, то белый цвет
}
} else {
colorValue = rev[i];
}
If column is 0 (first) I change color to white. If avg > 0 I try to get color of prev bar. Othewise I set default color from rev array.
Some changes:
function draw() {
requestAnimationFrame(draw);
const numBars = 5;
let bar = 0;
analyser.getByteFrequencyData(dataArray);
canvasCtx.fillStyle = "#000";
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
const barWidth = canvas.width / numBars;
for (let i = 0; i < numBars; i++) {
const start = Math.floor(i * bufferLength / numBars);
const end = Math.floor((i + 1) * bufferLength / numBars);
let sum = 0;
for (let j = start; j < end; j++) {
sum += dataArray[j];
}
const avg = sum / (end - start);
if (avg > 0) {
currentColors[i] = avg;
} else {
currentColors[i] = rev[i]; // Возвращаемся к исходному цвету
}
const rgbColor = `rgb(${currentColors[i]}, ${currentColors[i]}, ${currentColors[i]})`;
canvasCtx.fillStyle = rgbColor;
canvasCtx.fillRect(bar, 0, barWidth, canvas.height);
bar += barWidth;
}
}
2
Answers
I think this is what you are looking for but I may be wrong.
Explanation:
currentColors
, which will hold the current color of each bar since the old logic of updating thecolorValue
was not updating the colors of the bars properly.currentColors
array with the same initial colors as thedefaultBarColors
in theinitializeCanvas
function.If I understood you correctly it seems that you are trying to change the color of each bar based on the average frequency of the music and make sure that each bar inherits the color from the previous one under certain conditions. However, there may be a misunderstanding in the way the colors are calculated and applied.
Here is an updated version of your code that addresses the issues and ensures that the color of each bar matches the color of the previous bar when necessary. I also cleaned up some parts for clarity:
Explanation of what I did:
Color calculation: The color of each bar is determined by the current average frequency or inherits the color of the previous bar if the average frequency is zero.
Initialization: The initializeCanvas function sets initial colors for the stripes and stores them in the current array of colors.
Drawing logic: The drawing function ensures that if the average frequency of a bar is zero, it gets the color of the previous bar.
I hope I helped.