Skip to content

Instantly share code, notes, and snippets.

@outrageaudio
Created October 19, 2023 06:50
Show Gist options
  • Save outrageaudio/9222b7ffd7221dcf2e307c9c06405fd2 to your computer and use it in GitHub Desktop.
Save outrageaudio/9222b7ffd7221dcf2e307c9c06405fd2 to your computer and use it in GitHub Desktop.
Framer Code Component MP3 Player With Waveform (no Wavesurfer.js)
import * as React from "react"
import { Frame, addPropertyControls, ControlType } from "framer"
export function SamplePlayer(props) {
const canvasRef = React.useRef(null)
const [isPlaying, setIsPlaying] = React.useState(false)
const audioRef = React.useRef(new Audio(props.audioFile))
React.useEffect(() => {
if (props.audioFile) {
displayWaveform(props.audioFile)
audioRef.current.src = props.audioFile // Update the audio source
}
// Attach event listener
const handleAudioEnd = () => {
setIsPlaying(false)
}
audioRef.current.addEventListener("ended", handleAudioEnd)
// Cleanup on component unmount
return () => {
audioRef.current.removeEventListener("ended", handleAudioEnd)
}
}, [props.audioFile, props.width, props.height])
const togglePlay = () => {
if (isPlaying) {
audioRef.current.pause()
} else {
audioRef.current.play()
}
setIsPlaying(!isPlaying)
}
const displayWaveform = (fileUrl) => {
fetch(fileUrl)
.then((response) => response.arrayBuffer())
.then((arrayBuffer) => {
const context = new (window.AudioContext ||
window.webkitAudioContext)()
context.decodeAudioData(arrayBuffer, (buffer) => {
const data = buffer.getChannelData(0)
drawWaveform(data, canvasRef.current)
})
})
}
const drawWaveform = (data, canvas) => {
const context = canvas.getContext("2d")
const width = canvas.width
const height = canvas.height
const barWidth = 2
const gap = 1
context.clearRect(0, 0, width, height)
const numberOfBars = Math.floor(width / (barWidth + gap))
const samplesPerBar = Math.floor(data.length / numberOfBars)
for (let i = 0; i < numberOfBars; i++) {
let average = 0
for (let j = 0; j < samplesPerBar; j++) {
average += Math.abs(data[i * samplesPerBar + j])
}
average = average / samplesPerBar
const barHeight = average * height
context.fillRect(
i * (barWidth + gap),
(height - barHeight) / 2,
barWidth,
barHeight
)
}
}
return (
<Frame size="100%" width={props.width} height={props.height}>
<button
onClick={togglePlay}
style={{
position: "absolute",
left: 0,
top: "50%",
transform: "translateY(-50%)",
}}
>
{isPlaying ? "Pause" : "Play"}
</button>
<canvas
ref={canvasRef}
width={props.width}
height={props.height}
style={{ border: "1px solid", marginLeft: "60px" }}
/>
</Frame>
)
}
SamplePlayer.defaultProps = {
canvasWidth: 600,
canvasHeight: 200,
width: 600, // Default width when dragged onto the canvas
height: 200, // Default height when dragged onto the canvas
audioFile: "https://example.com/path-to-mp3.mp3", // Placeholder.
}
addPropertyControls(SamplePlayer, {
audioFile: {
type: ControlType.File,
title: "Audio File",
allowedFileTypes: ["mp3"],
},
canvasWidth: {
title: "Canvas Width",
type: ControlType.Number,
defaultValue: 600,
min: 100,
max: 2000,
step: 1,
},
canvasHeight: {
title: "Canvas Height",
type: ControlType.Number,
defaultValue: 200,
min: 50,
max: 1000,
step: 1,
},
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment