Creating an audio visualizer using HTML5 - javascript

I am trying to use an audio visualisation for the stream of my online radio using the example that I found on this page.
However, similar to the problem found in this post , my audio file (even when testing with a local file) just does not sound and of course the visualisation does nothing as well.
My HTML is the following:
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<audio src="http://50.22.218.101:38838/;steam.mp3" id="audio"
controls>HTML5 Audio element not supported</audio>
<canvas id="canvas" width="800" height="350"></canvas>
<script src="main.js"></script>
</body>
</html>
This is "main.js":
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
window.onload = function() {
var audio = document.getElementById('audio');
var ctx = new AudioContext();
var analyser = ctx.createAnalyser();
var audioSrc = ctx.createMediaElementSource(audio);
// we have to connect the MediaElementSource with the analyser
audioSrc.connect(analyser);
analyser.connect(ctx.destination);
// we could configure the analyser: e.g. analyser.fftSize (for further infos read the spec)
// analyser.fftSize = 64;
// frequencyBinCount tells you how many values you'll receive from the analyser
var frequencyData = new Uint8Array(analyser.frequencyBinCount);
// we're ready to receive some data!
var canvas = document.getElementById('canvas'),
cwidth = canvas.width,
cheight = canvas.height - 2,
meterWidth = 10, //width of the meters in the spectrum
gap = 2, //gap between meters
capHeight = 2,
capStyle = '#fff',
meterNum = 800 / (10 + 2), //count of the meters
capYPositionArray = []; ////store the vertical position of hte caps for the preivous frame
ctx = canvas.getContext('2d'),
gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(1, '#0f0');
gradient.addColorStop(0.5, '#ff0');
gradient.addColorStop(0, '#f00');
// loop
function renderFrame() {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
var step = Math.round(array.length / meterNum); //sample limited data from the total array
ctx.clearRect(0, 0, cwidth, cheight);
for (var i = 0; i < meterNum; i++) {
var value = array[i * step];
if (capYPositionArray.length < Math.round(meterNum)) {
capYPositionArray.push(value);
};
ctx.fillStyle = capStyle;
//draw the cap, with transition effect
if (value < capYPositionArray[i]) {
ctx.fillRect(i * 12, cheight - (--capYPositionArray[i]), meterWidth, capHeight);
} else {
ctx.fillRect(i * 12, cheight - value, meterWidth, capHeight);
capYPositionArray[i] = value;
};
ctx.fillStyle = gradient; //set the filllStyle to gradient for a better look
ctx.fillRect(i * 12 /*meterWidth+gap*/ , cheight - value + capHeight, meterWidth, cheight); //the meter
}
requestAnimationFrame(renderFrame);
}
renderFrame();
audio.play();
};
Any idea what am I doing wrong?

You can usually fix this by simply adding a crossorigin option to your html audio tag
so this:
<audio src="http://50.22.218.101:38838/;steam.mp3" id="audio" controls>
becomes this:
<audio src="http://50.22.218.101:38838/;steam.mp3" crossorigin="anonymous" id="audio" controls>

Visualize stream using HTML5
Here's full example, combining #miknik's answer, your display code (which is awesome by the way), and a public web radio station
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
let audio = document.getElementById('audio')
let ctx = ""
let audioSrc = ""
function start(){
if (!ctx){
ctx = new AudioContext();
analyser = ctx.createAnalyser();
audioSrc = ctx.createMediaElementSource(audio)
// we have to connect the MediaElementSource with the analyser
audioSrc.connect(analyser)
analyser.connect(ctx.destination)
}
// we could configure the analyser: e.g. analyser.fftSize (for further infos read the spec)
// analyser.fftSize = 64;
// frequencyBinCount tells you how many values you'll receive from the analyser
var frequencyData = new Uint8Array(analyser.frequencyBinCount);
// we're ready to receive some data!
var canvas = document.getElementById('canvas'),
cwidth = canvas.width,
cheight = canvas.height - 2,
meterWidth = 10, //width of the meters in the spectrum
gap = 2, //gap between meters
capHeight = 2,
capStyle = '#fff',
meterNum = 800 / (10 + 2), //count of the meters
capYPositionArray = []; ////store the vertical position of hte caps for the preivous frame
ctx = canvas.getContext('2d'),
gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(1, '#0f0');
gradient.addColorStop(0.5, '#ff0');
gradient.addColorStop(0, '#f00');
// loop
function renderFrame() {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
var step = Math.round(array.length / meterNum); //sample limited data from the total array
ctx.clearRect(0, 0, cwidth, cheight);
for (var i = 0; i < meterNum; i++) {
var value = array[i * step];
if (capYPositionArray.length < Math.round(meterNum)) {
capYPositionArray.push(value);
};
ctx.fillStyle = capStyle;
//draw the cap, with transition effect
if (value < capYPositionArray[i]) {
ctx.fillRect(i * 12, cheight - (--capYPositionArray[i]), meterWidth, capHeight);
} else {
ctx.fillRect(i * 12, cheight - value, meterWidth, capHeight);
capYPositionArray[i] = value;
};
ctx.fillStyle = gradient; //set the filllStyle to gradient for a better look
ctx.fillRect(i * 12 /*meterWidth+gap*/ , cheight - value + capHeight, meterWidth, cheight); //the meter
}
requestAnimationFrame(renderFrame);
}
renderFrame();
audio.play();
}
function stop(){
audio.pause()
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#container {
position: absolute;
top: 0;
left: 0;
background: #000;
width: 100%;
height: 100%;
}
#canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: black;
z-index: -1;
opacity: .8;
}
.button-container{
padding-top: 5px;
display: grid;
grid-template-columns: repeat(2,300px);
grid-gap: 1px;
width: 100%;
align-items: center;
justify-content: center;
}
button{
width: 300px;
text-align: center;
}
<html>
<link rel="stylesheet" type="text/css" href="style.css">
<body>
<audio src="https://rfcmedia.streamguys1.com/MusicPulse.mp3" crossorigin="anonymous" id="audio" >HTML5 Audio element not supported</audio>
<div class="button-container">
<button onclick="start()">Stream Radio</button>
<button onclick="stop()">Stop Radio</button>
</div>
<canvas id="canvas"></canvas>
<script src="main.js"></script>
</body>
</html>

Related

how to create pixelated video with pure java script

I am trying to create a blurred face video (when there were squares)
everything is ok until I minimize the size of the square then it becomes so laggy
and slow even some times the video stop working for example when the blur = 1 ;
this is my code :
<!DOCTYPE html>
<html>
<head>
<style>
#canvas{
width: 400px;
}
</style>
</head>
<body>
<video id="video" controls width="300" height="300"></video>
<canvas id="canvas" width="1280" height="720" ></canvas>
<script>
// Get references to the video and canvas elements
var video = document.getElementById("video");
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var videoSource = "111.mp4" ;
video.src = videoSource;
video.addEventListener("play", function() {
drawVideo();
});
function drawVideo() {
context.drawImage(video, 0, 0);
let blur = 20 ;
let originalImageData = context.getImageData( 0, 0, canvas.width, canvas.height ).data;
for (let y = 0; y < canvas.height; y += blur) {
for (let x = 0; x < canvas.width; x += blur) {
const pixelIndexPosition = (x + y * canvas.width) * 4;
context.fillStyle = `rgba(
${originalImageData[pixelIndexPosition]},
${originalImageData[pixelIndexPosition + 1]},
${originalImageData[pixelIndexPosition + 2]},
${originalImageData[pixelIndexPosition + 3]})`;
context.fillRect(x, y, blur, blur);
}
}
// Continue to draw the video
if (video.paused || video.ended) {
return;
}
requestAnimationFrame(drawVideo);
}
</script>
</body>
</html>

Equalizer bar for muted audio

I am trying to create an audio equalizer through the guidance of this tutorial.
This works fine until you mute the audio through the controls(as shown in the image). Muting it also stops the equalizer bars.
How would I go about implementing the equalizer bars so that the bars don't stop even when the audio is muted?
let canvas,
ctx,
source,
context,
analyser,
fbc_array,
bar_count,
bar_pos,
bar_width,
bar_height;
let audio = new Audio();
audio.src = "test.mp3";
audio.controls = true; // Show audio controls so we can pause and play at will
audio.loop = true;
audio.autoplay = true;
window.addEventListener(
"load", // only executes after loading page fully
function() {
document.getElementById("audio").appendChild(audio); // putting the audio element inside the another HTML element
audio.onplay = function() {
if (typeof(context) === "undefined") {
context = new AudioContext();
analyser = context.createAnalyser();
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d"); // returns a drawing context
source = context.createMediaElementSource(audio); // creates an audio object which can be manipulated
canvas.width = window.innerWidth * 0.80;
canvas.height = window.innerHeight * 0.60;
source.connect(analyser);
analyser.connect(context.destination); // connect the analyzer and the context(which is used to draw)
}
FrameLooper(); // will handle all the animation and output
};
},
false
);
function FrameLooper() {
window.requestAnimationFrame(FrameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount); // array for containing frequencies
bar_count = window.innerWidth / 2;
analyser.getByteFrequencyData(fbc_array);
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear old visual data
ctx.fillStyle = "#ffffff"; // fill it with white
/* For repainting */
for (let i = 0; i < bar_count; i++) {
bar_pos = i;
bar_width = 1;
bar_height = -(fbc_array[i] / 2);
ctx.fillRect(bar_pos, canvas.height, bar_width, bar_height);
}
}
body {
background: black;
padding: 0px;
margin: 0px;
}
div#player {
width: 80vw;
height: 60vh;
background: black;
margin: 20vh auto 0px auto;
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="styles.css">
<title>Equalizer bar</title>
</head>
<body>
<div id="player">
<div id="audio"></div>
<canvas id="canvas"></canvas>
</div>
</body>
<script type="text/javascript" src="functions.js"></script>
</html>

Load file and play audio on page load

I have have code that allows me to input an mp3 file, and output the audio and display a visual representation of the music. (Loading the file is done manually through an input type="file")
I no longer want to load a file from my computer, but instead automatically play the music on page load. The file is called song.mp3.
I not sure how to adjust my code to allow for the automatic play (I would like the visual representation to also work on load).
window.onload = function() {
var file = document.getElementById("thefile");
var audio = document.getElementById("audio");
file.onchange = function() {
var files = this.files;
audio.src = URL.createObjectURL(files[0]);
audio.load();
audio.play();
var context = new AudioContext();
var src = context.createMediaElementSource(audio);
var analyser = context.createAnalyser();
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
src.connect(analyser);
analyser.connect(context.destination);
analyser.fftSize = 256;
var bufferLength = analyser.frequencyBinCount;
console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var barWidth = (WIDTH / bufferLength) * 2.5;
var barHeight;
var x = 0;
function renderFrame() {
requestAnimationFrame(renderFrame);
x = 0;
analyser.getByteFrequencyData(dataArray);
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
var r = barHeight + (25 * (i/bufferLength));
var g = 250 * (i/bufferLength);
var b = 50;
ctx.fillStyle = "rgb(" + 0 + "," + 0 + "," + 0 + ")";
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
}
audio.play();
renderFrame();
};
};
#thefile {
position: fixed;
top: 10px;
left: 10px;
z-index: 100;
}
#canvas {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 50%;
-webkit-transform: rotate(180deg);
-moz-transform: rotate(180deg);
-o-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
audio {
position: fixed;
left: 10px;
bottom: 10px;
width: calc(100% - 20px);
}
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>JS Audio Visualizer</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="content">
<input type="file" id="thefile" accept="audio/*" />
<canvas id="canvas"></canvas>
<audio id="audio" controls></audio>
</div>
<script src="./script.js"></script>
</body>
</html>
You just need to load and play audio file after you got audio tag from document.
var audio = document.getElementById("audio");
audio.src = URL.createObjectURL("song.mp3");
audio.load();
audio.play();
Because Chrome don't allow autoplay audio onload until user has an interactive with the webpage. So you need to add an hidden autoplay iframe with silence sound.
<iframe src="silence.mp3" allow="autoplay" id="audio" style="display:none"></iframe>
You can get silence sound file from here https://github.com/anars/blank-audio/blob/master/250-milliseconds-of-silence.mp3
Your js
window.onload = function() {
function renderVisual() {
var analyser = context.createAnalyser();
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
src.connect(analyser);
analyser.connect(context.destination);
analyser.fftSize = 256;
var bufferLength = analyser.frequencyBinCount;
console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);
var WIDTH = canvas.width;
var HEIGHT = canvas.height;
var barWidth = (WIDTH / bufferLength) * 2.5;
var barHeight;
var x = 0;
function renderFrame() {
requestAnimationFrame(renderFrame);
x = 0;
analyser.getByteFrequencyData(dataArray);
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
var r = barHeight + (25 * (i/bufferLength));
var g = 250 * (i/bufferLength);
var b = 50;
ctx.fillStyle = "rgb(" + 0 + "," + 0 + "," + 0 + ")";
ctx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
x += barWidth + 1;
}
}
renderFrame();
}
var context = new AudioContext();
var file = document.getElementById("thefile");
var audio = document.getElementById("audio");
audio.src = "song.mp3";
audio.load();
var src = context.createMediaElementSource(audio);
audio.play();
renderVisual();
file.onchange = function() {
var files = this.files;
audio.src = URL.createObjectURL(files[0]);
audio.load();
audio.play();
renderVisual();
};
};
Your html
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>JS Audio Visualizer</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="content">
<input type="file" id="thefile" accept="audio/*" />
<canvas id="canvas"></canvas>
<audio id="audio" controls></audio>
<iframe src="silence.mp3" allow="autoplay" id="silence_audio" style="display:none"></iframe>
</div>
<script src="./script.js"></script>
</body>
</html>
Note: This will get error on Chrome if you open file directly on your local because of CORS. You need to host your website to a server (Nginx or Apache).
Here is the easiest way to play mp3 on page load
You just need to add control and autoplay attributes on the audio tag and then you can play autoplay music
<audio id="myAudio" controls autoplay>
<source src="music.mp3" type="audio/mpeg">
</audio>
Note: It will not work in local you need to host your file on the server
Reference Link: https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_autoplay

Javascript Audio API Visualizer not showing in canvas

So i followed this tutorial: https://youtu.be/IBHpSkGZtNM
And a while back (about a year ago or so) it worked. Now however it doesn't visualize anything. The sound plays but thats it.
I changed the old webkit functions to the new ones as i was getting errors. However now i don't have any errors and still nothing shows up.
Here is my code:
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
div#mp3_player{ width: 500px; height: 60px; background: #000000; padding: 5px; margin: 50px auto;}
div#mp3_player > div > audio{ width: 500px; background: #000000; float: left;}
div#mp3_player > canvas{width: 500px; height: 30px; background: #002D3C; float: left;}
</style>
<script>
var audio = new Audio();
audio.src = "audio/DieForYou-Starset.mp3";
audio.controls = true;
audio.loop = true;
audio.autoplay = false;
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
window.addEventListener("load", initMp3Player, false);
function initMp3Player(){
document.getElementById("audio_box").appendChild(audio);
context = new AudioContext();
analyser = context.createAnalyser();
canvas = document.getElementById("analyser_renderer");
ctx = canvas.getContext("2d");
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
frameLooper()
}
function frameLooper(){
window.requestAnimationFrame(frameLooper);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
ctx.clearRect(0,0 ,canvas.width, canvas.height)
ctx.fillStyle= "#2DCC70";
bars = 100;
for(var i = 0; i < bars; i++){
bar_x = i * 3;
bar_width = 2;
bar_height = -(fbc_array[1] / 2);
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
</script>
</head>
<body>
<div id="mp3_player">
<div id="audio_box"></div>
<canvas id="analyser_renderer"></canvas>
</div>
</body>
</html>
I put everything in the html file because it was easier to post like that. I know it's bad practise.
Note: the sound wont play in the snippet because the audio file is stored locally.
I am also running this trough a local server as just opening the html file would result in MediaElementAudioSource outputs zeroes due to CORS access restrictions for file:///C:/xampp/htdocs/audio/DieForYou-Starset.mp3
You need to populate your array with one of the AnalyserNode methods, here you are only creating an empty one.
For example, you can call analyser.getByteFrequencyData(fbc_array); to get the current frequency data.
var audio = new Audio();
audio.crossOrigin = 'anonymous';
audio.src = "https://dl.dropboxusercontent.com/s/8c9m92u1euqnkaz/GershwinWhiteman-RhapsodyInBluePart1.mp3";
audio.controls = true;
audio.loop = true;
audio.autoplay = false;
window.onload = initMp3Player;
var canvas, ctx, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height;
function initMp3Player() {
document.getElementById("audio_box").appendChild(audio);
context = new AudioContext();
analyser = context.createAnalyser();
canvas = document.getElementById("analyser_renderer");
ctx = canvas.getContext("2d");
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
// initialise the Uint8Array only once
fbc_array = new Uint8Array(analyser.frequencyBinCount);
frameLooper()
}
function frameLooper() {
window.requestAnimationFrame(frameLooper);
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "#2DCC70";
bars = 100;
// here fill the Uint8Array with audio data
analyser.getByteFrequencyData(fbc_array);
for (var i = 0; i < bars; i++) {
bar_x = i * 3;
bar_width = 2;
bar_height = -(fbc_array[i] / 2); // there was a typo
ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
}
}
div#mp3_player {
width: 500px;
height: 60px;
background: #000000;
padding: 5px;
margin: 50px auto;
}
div#mp3_player>div>audio {
width: 500px;
background: #000000;
float: left;
}
div#mp3_player>canvas {
width: 500px;
height: 30px;
background: #002D3C;
float: left;
}
<div id="mp3_player">
<div id="audio_box"></div>
<canvas id="analyser_renderer"></canvas>
</div>

drawImage() not working consistently

When you put a picture named logo.png in the same directory as this html file and try to run it in a web browser the picture only appears 1 times out of 10 refreshes in IE and doesn't appear the first time in Firefox but does appear after further refreshes.
What the heck is going on ?
(drawImage() method is called in the showIntro() function)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Example 1 - Title Screen</title>
<script>
window.onload = function () {
var canvas = document.getElementById('myCanvas');
var c = canvas.getContext('2d');
var State = {
_current: 0,
INTRO: 0,
LOADING: 1,
LOADED: 2
}
window.addEventListener('click', handleClick, false);
window.addEventListener('resize', doResize, false);
doResize();
function handleClick() {
State._current = State.LOADING;
fadeToWhite();
}
function doResize() {
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
switch (State._current) {
case State.INTRO:
showIntro();
break;
}
}
function fadeToWhite(alphaVal) {
// If the function hasn't received any parameters, start with 0.02
var alphaVal = (alphaVal == undefined) ? 0.02 : parseFloat(alphaVal) + 0.02;
// Set the color to white
c.fillStyle = '#FFFFFF';
// Set the Global Alpha
c.globalAlpha = alphaVal;
// Make a rectangle as big as the canvas
c.fillRect(0, 0, canvas.width, canvas.height);
if (alphaVal < 1.0) {
setTimeout(function () {
fadeToWhite(alphaVal);
}, 30);
}
}
function showIntro() {
var phrase = "Click or tap the screen to start the game";
// Clear the canvas
c.clearRect(0, 0, canvas.width, canvas.height);
// Make a nice blue gradient
var grd = c.createLinearGradient(0, canvas.height, canvas.width, 0);
grd.addColorStop(0, '#ceefff');
grd.addColorStop(1, '#52bcff');
c.fillStyle = grd;
c.fillRect(0, 0, canvas.width, canvas.height);
var logoImg = new Image();
logoImg.src = './logo.png';
// Store the original width value so that we can keep
// the same width/height ratio later
var originalWidth = logoImg.width;
// Compute the new width and height values
logoImg.width = Math.round((50 * document.body.clientWidth) / 100);
logoImg.height = Math.round((logoImg.width * logoImg.height) / originalWidth);
// Create an small utility object
var logo = {
img: logoImg,
x: (canvas.width / 2) - (logoImg.width / 2),
y: (canvas.height / 2) - (logoImg.height / 2)
}
// Present the image
c.drawImage(logo.img, logo.x, logo.y, logo.img.width, logo.img.height);
// Change the color to black
c.fillStyle = '#000000';
c.font = 'bold 16px Arial, sans-serif';
var textSize = c.measureText(phrase);
var xCoord = (canvas.width / 2) - (textSize.width / 2);
c.fillText(phrase, xCoord, (logo.y + logo.img.height) + 50);
}
}
</script>
<style type="text/css" media="screen">
html { height: 100%; overflow: hidden }
body {
margin: 0px;
padding: 0px;
height: 100%;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="100" height="100">
Your browser doesn't include support for the canvas tag.
</canvas>
</body>
</html>
The problem is that you aren't waiting for the image to load when you call drawImage().
You could use something like:
logo.img.onload = function(){
c.drawImage(logo.img, logo.x, logo.y, logo.img.width, logo.img.height);
};
Although make sure you don't start modifying the canvas until this has happened.

Categories