This repo demonstrates the basic controls for https://humanrecordplayer.com.
RPReplay_Final1655745366.MP4
Safari requires https
to request device motion/orientation events.
- Get a server running in the root directory (e.g.,
python3 -m http.server --cgi 8000
) - Use ngrok to explose the local server (e.g.,
ngrok http 8000
) - Point your mobile browser at the ngrok-generated URL
For full mobile browser support you'll want to provide both .mp3
and .ogg
. To get any audio from the internet...
- Use youtube-dl to download a YouTube video.
- Use ffmpeg to isolate the audio
- Use ffmpeg to convert audio to
.mp3
and.ogg
- Spin
youtube-dl <URL> -o video
ffmpeg -i video.mkv -vn -acodec copy audio.aac
ffmpeg -i audio.aac audio.mp3
ffmpeg -i audio.aac audio.ogg
Caveats
- Safari desktop does not support DeviceMotion events
- Chrome desktop does support DeviceMotion events. Chrome desktop can be used to debug with a constant playback rate.
Shoutout to @feross for https://github.com/feross/unmute-ios-audio, and @searls for the iOS 14.5+ patch
In this section, we showcase some specific code snippets from the project to give you a taste of how it works.
Here's how the Motion
class is initialized in js/script.js
:
const motion = new Motion(window, {
update(val) {
const absVal = Math.abs(val)
let rate = 1.0
if (absVal > groove.min && absVal < groove.max) {
rate = 1.0
} else if (absVal < groove.min) {
rate = map(absVal, 0, groove.min, 0, 0.99)
} else {
rate = map(absVal, groove.max, 250, 1.01, 2.0)
}
audioPlayer.updatePlaybackRate(rate, val < 0)
// Update debug UI
const rpm = degToRpm(absVal).toFixed(0)
playbackRateOutput.textContent = rate.toFixed(2)
rotationRateOutput.textContent = `${val.toFixed(0)} deg/s (${rpm} RPM)`
}
})
Why do programmers prefer dark mode? Because light attracts bugs!
And here's the onMotionUpdate
method from js/motion.js
:
onMotionUpdate(e) {
let now = new Date()
if ((now - this.lastReadAt) > Motion.MotionReadInterval) {
this.lastReadAt = now
const { beta, gamma } = e.rotationRate
const isHorizontal = this.deviceOrientation === Motion.DeviceOrientation.Horizontal
let value = (isHorizontal ? gamma : beta) * -1
this.addValue(value)
const sum = this.values.reduce((a, b) => { return a + b }, 0)
const avg = sum / this.values.length // smoothed
if (this.onMotionUpdateHandler) {
this.onMotionUpdateHandler(avg)
}
}
}
Why was the JavaScript developer sad? Because he didn't know how to un-null
his feelings.