Home > database >  i am trying to do an audio visualization with HTML canvas
i am trying to do an audio visualization with HTML canvas

Time:10-28

i did implement the audio visualization but i am not happy with the results. i was hoping for a better solution than what i came up with. like for example is there a better way to draw the bars and also a better way to animate other than what i did which is changing the gradient.

audio track before play

this is the audio track before the play button is clicked

after play

this is the audio track after play button is clicked

here is the code

window.addEventListener('load', init)

const canvas = document.querySelector('canvas')
const btn = document.querySelector('#play')

const ctx = canvas.getContext('2d')

const sample = [...] // this has a random height values between 0 and 24 px
function init() {

        let barLength = 55
    let barWidth = (canvas.width - (barLength - 1)) / barLength 
    let region = new Path2D()
    for(let i = 0; i < sample.length; i  ) {
        let halfHeight = sample[i] / 2
        region.rect(i * (barWidth   1), (24 - halfHeight), barWidth, sample[i])
    }

    var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
    grd.addColorStop(0, "#ddd")     
    grd.addColorStop(1, "#ddd")

    ctx.clip(region)

    ctx.fillStyle = grd
    ctx.fillRect(0, 0, canvas.width, canvas.height)
}


let xPos = 0
function drawRect() {
    ctx.clearRect(0, 0, canvas.width, canvas.height)

    let pos = xPos / canvas.width

        pos = pos > 1.0 ? 1 : pos

        var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
    grd.addColorStop(0, "orange")
    grd.addColorStop(pos, "orange")
    grd.addColorStop(pos, "#ddd")
    grd.addColorStop(1, "#ddd")

    ctx.fillStyle = grd
        ctx.fillRect(0, 0, canvas.width, canvas.height)
}

let prev
let requestId
function draw(sec) {
    if (!prev)
           prev = new Date()

        let now  = new Date()
  
        let difference = now - prev

        prev = now

        xPos  = (difference * canvas.width) / (duration * 1000)

        drawRect()

    requestId = requestAnimationFrame(draw)

    if(xPos >= canvas.width) {
        prev = null
        cancelAnimationFrame(requestId)
    }
}

btn.addEventListener('click', () => {
    draw()
})

CodePudding user response:

Your idea is pretty good.

The only thing I could think of is that you are currently redrawing the full image every frame, but that just changes the orange part.

What you could do instead is draw "holes" into the picture as to have the white bars be fully transparent. That way, we would see through the waves. The picture would then be in a div and its background would be changed to have the orange highlight.

This in no way changes the visual, it just reduces the computations needed for the same effect.

const canvas = document.querySelector('canvas')
const btn = document.querySelector('#play')

const ctx = canvas.getContext('2d')

const sample = []; // this has a random height values between 0 and 24 px
for(let i=0;i<55;i  ){
sample.push(Math.random()*24);
}
function init() {
    ctx.fillRect(0,0, canvas.width, canvas.height);
        let barLength = 55
    let barWidth = (canvas.width - (barLength - 1)) / barLength 
    let region = new Path2D()
    for(let i = 0; i < sample.length; i  ) {
        let halfHeight = sample[i] / 2
        region.rect(i * (barWidth   1), (24 - halfHeight), barWidth, sample[i])
    }

    var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
    grd.addColorStop(0, "#ddd")     
    grd.addColorStop(1, "#ddd")

    ctx.clip(region);
    ctx.globalCompositeOperation = 'destination-atop';
    ctx.globalAlpha = 0;
    ctx.fillRect(0, 0, canvas.width, canvas.height)
}


let xPos = 0
function drawRect() {

    ctx.clearRect(0, 0, canvas.width, canvas.height)

    let pos = xPos / canvas.width

        pos = pos > 1.0 ? 1 : pos

        var grd = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
    grd.addColorStop(0, "orange")
    grd.addColorStop(pos, "orange")
    grd.addColorStop(pos, "#ddd")
    grd.addColorStop(1, "#ddd")

    ctx.fillStyle = grd
        ctx.fillRect(0, 0, canvas.width, canvas.height)
}
init();
const div = document.querySelector('#div');
setInterval(function(){
xPos  ;
 const percent = xPos / canvas.width *100;
 const t = `linear-gradient(90deg, rgba(255,100,0,1), rgba(255,100,0,1) ${percent}%, rgba(255,255,255,1) ${percent}%, rgba(255,255,255,1) 100%)`;
div.style.backgroundImage = t;
},100);
div{
    background-color:white;
    display:inline-block;
    height:50px;
}
<div id="div"><canvas height="50"></canvas></div>

  • Related