from fasthtml.common import * from fasthtml.jupyter import * from monsterui.all import *
audrey.feldroy.com
The experimental notebooks of Audrey M. Roy Greenfeld. This website and all its notebooks are open-source at github.com/audreyfeldroy/audrey.feldroy.com
# Alarm Clock Sounds
by Audrey M. Roy Greenfeld | Fri, Jan 17, 2025
FastHTML MonsterUI example app that uses Tone.js to make different alarm clock sounds.
The backstory: I've been adjusting my sleep schedule to match up better with my coworkers. This afternoon I found myself quite sleepy at the wrong time. I made this as a little utility for myself.
Setup
app,rt = fast_app(hdrs=(Theme.blue.headers(),Script(src="https://unpkg.com/tone")))
server = JupyUvi(app)
Sounds JS
Note: I may break this up, putting the code for each function into its corresponding FT. I feel like that would be a little cleaner.
sounds_js = """const synth = new Tone.PolySynth().toDestination(); const pingPong = new Tone.PingPongDelay("4n", 0.2).toDestination(); synth.connect(pingPong); let currentInterval; // Classic alarm sound (repeating high-pitched beeps) function playClassicAlarm() { stopCurrentSound(); currentInterval = setInterval(() => { synth.triggerAttackRelease("C5", "16n"); setTimeout(() => { synth.triggerAttackRelease("G4", "16n"); }, 200); }, 400); } // Digital alarm sound (ascending beeps) function playDigitalAlarm() { stopCurrentSound(); const notes = ["C4", "E4", "G4", "C5"]; let index = 0; currentInterval = setInterval(() => { synth.triggerAttackRelease(notes[index % notes.length], "8n"); index++; }, 200); } // Gentle wake sound (soft pulsing chord) function playGentleAlarm() { stopCurrentSound(); const chord = ["C4", "E4", "G4"]; currentInterval = setInterval(() => { synth.volume.value = -10; synth.triggerAttackRelease(chord, "2n"); }, 2000); } // Stop all sounds function stopCurrentSound() { if (currentInterval) { clearInterval(currentInterval); currentInterval = null; } synth.volume.value = 0; }"""
alarms = ( ('Classic', 'Repeating high-pitched beeps', 'playClassicAlarm'), ('Digital Beep', 'Ascending beeps', 'playDigitalAlarm'), ('Gentle Wake', 'Soft pulsing chord', 'playGentleAlarm') )
SoundBar Page
def SoundBar(): mbrs1 = [Li('Classic', onmousedown="playClassicAlarm()"), Li('Digital Beep', onmousedown="playDigitalAlarm()"), Li('Gentle Wake', onmousedown="playGentleAlarm()"), Li('Stop', onmousedown="stopCurrentSound()")] return NavContainer(*mbrs1)
SoundBar()
<ul class="uk-nav uk-nav-primary">
<li onmousedown="playClassicAlarm()">Classic</li>
<li onmousedown="playDigitalAlarm()">Digital Beep</li>
<li onmousedown="playGentleAlarm()">Gentle Wake</li>
<li onmousedown="stopCurrentSound()">Stop</li>
</ul>
ul((li(('Classic',),{'onmousedown': 'playClassicAlarm()'}), li(('Digital Beep',),{'onmousedown': 'playDigitalAlarm()'}), li(('Gentle Wake',),{'onmousedown': 'playGentleAlarm()'}), li(('Stop',),{'onmousedown': 'stopCurrentSound()'})),{'uk-nav': False, 'class': 'uk-nav uk-nav-primary'})@rt def index(): return Titled("Alarm Clock Sounds", SoundBar(), Script(sounds_js))
Click on the first 3 to play their sounds, and "Stop" to stop:
# HTMX(index)
At this point I have clickable Li
elements with onmousedown
handlers. I could call this done if my goal is just to play different alarm clock sounds.
Better Alarm Display
Let's take it up a level with UI improvements with MonsterUI.
alarms[0]
Card?
c = Card((Img(alarms[0][1])), header=H2(alarms[0][0]), onmousedown=f"{alarms[0][2]}()") c
<div onmousedown="playClassicAlarm()" class="uk-card ">
<div class="uk-card-header ">
<h2 class="uk-h2 ">Classic</h2>
</div>
<div class="uk-card-body space-y-6">
<img>Repeating high-pitched beeps </div>
</div>
div((div((h2(('Classic',),{'class': 'uk-h2 '}),),{'class': 'uk-card-header '}), div((img(('Repeating high-pitched beeps',),{}),),{'class': 'uk-card-body space-y-6'})),{'onmousedown': 'playClassicAlarm()', 'class': 'uk-card '})show(c)
Classic
Note: It would be nice if the card looked like a MonsterUI card here, so I could build them up cell by cell. Instead let's look at it by redefining the index handler.
@rt def index(): return Titled("Alarm Clock Sounds", c, Script(sounds_js))
It works when I click on it on the index page. In the notebook it doesn't. Maybe I will break up the JS to fix that. Maybe.
But first let's show all the cards.
def AlarmCard(alarm): return Card((Img(alarm[1])), header=H2(alarm[0]), onmousedown=f"{alarm[2]}()") show(AlarmCard(alarms[2]))
Gentle Wake
@rt def index(): return Titled("Alarm Clock Sounds", *[AlarmCard(a) for a in alarms], Script(sounds_js))
# HTMX(index)
@rt def index(): return Titled("Alarm Clock Sounds", *[AlarmCard(a) for a in alarms], Card("Stop the current alarm", header=H2("STOP"), onmousedown="stopCurrentSound()"), Script(sounds_js))
# HTMX(index)
@rt def index(): return Titled("Alarm Clock Sounds", DivFullySpaced( *[AlarmCard(a) for a in alarms], Card("Stop the current alarm", header=H2("STOP"), onmousedown="stopCurrentSound()"), ), Script(sounds_js))
# HTMX(index)
@rt def index(): return Titled("Alarm Clock Sounds", DivFullySpaced( Div( *[AlarmCard(a) for a in alarms], ), Card("Stop the current alarm", header=H2("STOP"), onmousedown="stopCurrentSound()"), ), Script(sounds_js))
# HTMX(index)
Stop the Server
server.stop()
© 2024-2025 Audrey M. Roy Greenfeld