Download components/visualizer.js from kingarnica/echosphere-audio-visualizer: direct link, hf CLI and curl.
- Browser
- Download file 7.38 kB
-
https://huggingface.co/spaces/kingarnica/echosphere-audio-visualizer/resolve/main/components/visualizer.js
- Command line
-
hf download hf://spaces/kingarnica/echosphere-audio-visualizer/components/visualizer.js
-
curl -L -o visualizer.js https://huggingface.co/spaces/kingarnica/echosphere-audio-visualizer/resolve/main/components/visualizer.js
7.38 kB
| class AudioVisualizer extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.analyser = null; | |
| this.animationId = null; | |
| this.dataArray = null; | |
| this.type = 'bars'; | |
| this.canvas = document.createElement('canvas'); | |
| this.ctx = this.canvas.getContext('2d'); | |
| } | |
| static get observedAttributes() { | |
| return ['type', 'static']; | |
| } | |
| attributeChangedCallback(name, oldValue, newValue) { | |
| if (name === 'type') { | |
| this.type = newValue; | |
| if (this.animationId) { | |
| cancelAnimationFrame(this.animationId); | |
| this.startVisualization(); | |
| } | |
| } | |
| } | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| // Set canvas dimensions to match the component | |
| this.canvas.width = this.clientWidth; | |
| this.canvas.height = this.clientHeight; | |
| this.shadowRoot.appendChild(this.canvas); | |
| // Initialize the data array with the correct size | |
| if (this.analyser) { | |
| this.dataArray = new Uint8Array(this.analyser.frequencyBinCount); | |
| } | |
| // If static mode, generate demo data | |
| if (this.hasAttribute('static')) { | |
| this.dataArray = new Uint8Array(128); | |
| this.generateDemoData(); | |
| this.startVisualization(); | |
| } | |
| } | |
| generateDemoData() { | |
| const now = Date.now() / 1000; | |
| for (let i = 0; i < this.dataArray.length; i++) { | |
| // Generate some synthetic frequency data for demo purposes | |
| const pos = i / this.dataArray.length; | |
| const timeFactor = Math.sin(now + pos * Math.PI * 2) * 0.5 + 0.5; | |
| const freqFactor = Math.pow(1 - pos, 1.5) + 0.1 * Math.random(); | |
| this.dataArray[i] = 64 + Math.round(150 * timeFactor * freqFactor); | |
| } | |
| return this.dataArray; | |
| } | |
| setAnalyser(analyser) { | |
| this.analyser = analyser; | |
| if (this.analyser) { | |
| this.dataArray = new Uint8Array(this.analyser.frequencyBinCount); | |
| } | |
| } | |
| startVisualization() { | |
| if (!this.analyser) return; | |
| const draw = () => { | |
| this.analyser.getByteFrequencyData(this.dataArray); | |
| this.clearCanvas(); | |
| switch(this.type) { | |
| case 'bars': | |
| this.drawBars(); | |
| break; | |
| case 'wave': | |
| this.drawWave(); | |
| break; | |
| case 'circle': | |
| this.drawCircle(); | |
| break; | |
| case 'matrix': | |
| this.drawMatrix(); | |
| break; | |
| case 'particles': | |
| this.drawParticles(); | |
| break; | |
| case 'lissajous': | |
| this.drawLissajous(); | |
| break; | |
| case 'radial': | |
| this.drawRadial(); | |
| break; | |
| case 'polar': | |
| this.drawPolar(); | |
| break; | |
| case 'vortex': | |
| this.drawVortex(); | |
| break; | |
| default: | |
| this.drawBars(); | |
| } | |
| this.animationId = requestAnimationFrame(draw); | |
| }; | |
| draw(); | |
| } | |
| stopVisualization() { | |
| if (this.animationId) { | |
| cancelAnimationFrame(this.animationId); | |
| this.animationId = null; | |
| this.clearCanvas(); | |
| } | |
| } | |
| clearCanvas() { | |
| this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| } | |
| drawBars() { | |
| const width = this.canvas.width; | |
| const height = this.canvas.height; | |
| const barWidth = width / this.dataArray.length; | |
| const gradient = this.ctx.createLinearGradient(0, 0, 0, height); | |
| gradient.addColorStop(0, '#4f46e5'); | |
| gradient.addColorStop(1, '#c026d3'); | |
| this.ctx.fillStyle = gradient; | |
| for (let i = 0; i < this.dataArray.length; i++) { | |
| const barHeight = (this.dataArray[i] / 255) * height; | |
| const x = i * barWidth; | |
| const y = height - barHeight; | |
| // Add some rounded corners to the bars | |
| this.ctx.beginPath(); | |
| this.ctx.moveTo(x + barWidth / 2, y); | |
| this.ctx.arcTo(x + barWidth, y, x + barWidth, y + barHeight, 2); | |
| this.ctx.arcTo(x + barWidth, y + barHeight, x, y + barHeight, 2); | |
| this.ctx.arcTo(x, y + barHeight, x, y, 2); | |
| this.ctx.arcTo(x, y, x + barWidth / 2, y, 2); | |
| this.ctx.closePath(); | |
| this.ctx.fill(); | |
| } | |
| } | |
| drawWave() { | |
| const width = this.canvas.width; | |
| const height = this.canvas.height; | |
| const sliceWidth = width / this.dataArray.length; | |
| this.ctx.lineWidth = 2; | |
| this.ctx.strokeStyle = '#818cf8'; | |
| this.ctx.beginPath(); | |
| let x = 0; | |
| for (let i = 0; i < this.dataArray.length; i++) { | |
| const v = this.dataArray[i] / 255; | |
| const y = v * height / 2; | |
| if (i === 0) { | |
| this.ctx.moveTo(x, y); | |
| } else { | |
| this.ctx.lineTo(x, y); | |
| } | |
| x += sliceWidth; | |
| } | |
| this.ctx.lineTo(width, height / 2); | |
| this.ctx.stroke(); | |
| } | |
| drawCircle() { | |
| const centerX = this.canvas.width / 2; | |
| const centerY = this.canvas.height / 2; | |
| const radius = Math.min(this.canvas.width, this.canvas.height) * 0.4; | |
| this.ctx.lineWidth = 2; | |
| for (let i = 0; i < this.dataArray.length; i++) { | |
| const amplitude = this.dataArray[i] / 255; | |
| const angle = (i / this.dataArray.length) * Math.PI * 2; | |
| const pointRadius = radius + (amplitude * radius * 0.5); | |
| const x = centerX + Math.cos(angle) * pointRadius; | |
| const y = centerY + Math.sin(angle) * pointRadius; | |
| // Gradient color based on frequency | |
| const gradient = this.ctx.createRadialGradient(x, y, 0, x, y, 5); | |
| gradient.addColorStop(0, '#8b5cf6'); | |
| gradient.addColorStop(1, '#3b82f6'); | |
| this.ctx.beginPath(); | |
| this.ctx.arc(x, y, 3, 0, Math.PI * 2); | |
| this.ctx.fillStyle = gradient; | |
| this.ctx.fill(); | |
| // Draw connecting lines for a more dynamic effect | |
| if (i > 0) { | |
| const prevAngle = ((i - 1) / this.dataArray.length) * Math.PI * 2; | |
| const prevAmplitude = this.dataArray[i - 1] / 255; | |
| const prevPointRadius = radius + (prevAmplitude * radius * 0.5); | |
| const prevX = centerX + Math.cos(prevAngle) * prevPointRadius; | |
| const prevY = centerY + Math.sin(prevAngle) * prevPointRadius; | |
| this.ctx.beginPath(); | |
| this.ctx.moveTo(prevX, prevY); | |
| this.ctx.lineTo(x, y); | |
| this.ctx.strokeStyle = `rgba(139, 92, 246, ${0.5 + amplitude * 0.5})`; | |
| this.ctx.stroke(); | |
| } | |
| } | |
| } | |
| } | |
| customElements.define('audio-visualizer', AudioVisualizer); |