by Rahel Lüthy
In preparation for a MaKey-MaKey-workshop, I needed a simple HTML page which plays a sound whenever any key is pressed. I am using all sorts of excuses to write Elm lately, so here’s how I ended up triggering the audio clip (full sources on GitHub, deployed version here).
First, we need a signal that indicates whether any key is currently down:
isAnyKeyDown : Signal Bool
isAnyKeyDown =
Signal.map (\keys -> not (Set.isEmpty keys)) Keyboard.keysDown
For debugging, let’s visualize our events by displaying a small note image:
view : Bool -> Html
view isAnyKeyDown =
if | isAnyKeyDown -> img [ src "note.png" ] []
| otherwise -> div [] []
main : Signal Html
main =
Signal.map view isAnyKeyDown
Next, we need to actually play the audio. Elm itself does not support this out-of-the-box, but triggering it via HTML5 and JavaScript is quite straight forward:
var audio = document.getElementById('audio')
function playAudio() {
// reset if already running
audio.pause();
audio.currentTime = 0;
// go!
audio.play();
}
Finally, we need to wire our JS function to our Elm signal. This is where Elm ports come in:
port playAudio : Signal ()
port playAudio =
Signal.map (\_ -> ()) (Signal.filter identity False isAnyKeyDown)
There are two things to note here: First, we’re filtering our isAnyKeyDown
to no longer contain key release events. Second, we’re mapping our signal of boolean values to a signal of unit types ()
to match the signature of our parameter-less JS function.
All there is left, is to actually trigger the JS callback whenever Elm sends an event: