Adding sound to Curly Logo was a bit of a challenge, but it now has a beep command. I’m sorry the beep is not very pleasing; it was easy for me to write a Python program to synthesize the .wav file, and it compresses very well.
The beep command works by creating an HTML embed element that specifies a sound file, then creating a span element to contain the embed element, then displaying the span element. The act of displaying the span causes the sound to play. Without additional styling that also causes a great big ugly span containing an embedded play controller to appear. So I style the span before displaying it:
span.style.position = "absolute" span.style.visibility = "hidden" span.style.top = '0px'
Giving it «position: absolute» removes the span from normal flow, so its appearance doesn’t affect the position of anything else. Giving it «visibility: hidden» makes it invisible. Normally it makes something invisible whilst still preserving their effect on normal flow; that text will still flow around an invisible floating image for example. I use «position: absolute» so the element has no effect on normal flow anyway.
Contrast this with «display: none» which means that the element is not displayed, as opposed to being displayed outside of normal flow and invisible. Whilst this might seem like mere semantics, the difference is crucial. Using «display: none» will not make the sound play, whereas «position: absolute;visibility: hidden» does play the sound.
«top: 0px» avoids a bug in Safari where it expands the drawn area and adds a scrollbar even though it doesn’t draw anything there. Firefox seems to have a similar bug that I haven’t worked round.
Playing the sound again involves a little trick. First I tried «span.style.display=’none’» followed by «span.style.display=’inline’». This works when done “by hand” but fails when put into a function and called. Presumably Gecko needs to trigger a reflow between the two assignments, otherwise it just observes that nothing has changed so no redisplay is necessary. So what I do is throwaway the current span containing the embed element and create a new span. This is a good justification for having the span/embed pair, we can throwaway and recreate a relatively lightweight span, but the more heavyweight embed element is reused.
RFC 2397 to the rescue!
[Update: 2008-01-10: Now I use MIDI not .wav; this is better in several ways. I still use the RFC 2397 encoding.]
Of course I should be using an object element not an embed element, as embed is not standard. Couldn’t get object elements to work in Safari. Hush now.