A small JavaScript library that lets an HTML page follow, and control, the Too Fast Mac app. Use it to put the speaking pace into your own presentation: turn the slide red when you rush, show a live gauge, start listening when you reach a slide.
It is one file, toofast.js, with no build step and no dependencies. It talks to the Mac app over your local network.
192.168.1.20).<script src="http://192.168.1.20:4321/sdk/toofast.js"></script>
<script>
const tf = new TooFast({ host: '192.168.1.20', code: '123456' });
tf.on('state', (state) => {
document.body.dataset.pace = state; // 'fast', 'warn', 'ok', 'listening' or 'idle'
});
tf.on('tick', (t) => {
document.querySelector('#rate').textContent = t.smoothed?.toFixed(1) ?? '–';
});
tf.connect();
</script>
tf.start()), and talk.example.html is a complete working page. Open it as http://MAC-ADDRESS:4321/sdk/example.html?code=123456.
If you would rather keep the file next to your presentation, copy toofast.js there and use <script src="toofast.js">. It only needs to reach the Mac over the network.
Everything the SDK does needs the pairing code from the Mac app's Phone page. Anyone on the same Wi‑Fi without the code cannot read or control your Mac.
code when you create the client.host: 'localhost' and leave code out. See On the same Mac.error event with type auth.error type locked). The SDK stops retrying when it sees a refusal, so a wrong code never loops.Treat the code like a password for your Mac's microphone controls. Do not commit it to a public repository or publish a presentation that contains it.
When your presentation runs on the same Mac as the Too Fast app, skip the code:
const tf = new TooFast({ host: 'localhost' });
tf.connect();
This works from a page served by localhost or 127.0.0.1 (a local dev server for your slides, for example) and from scripts such as curl. It does not work for a website you have open in your browser: any page can reach localhost from your browser, so the Mac only skips the code when the request cannot come from another website. A page loaded from https://example.com still needs the code, even when it talks to localhost, and so does a page opened straight from a file (file://).
new TooFast(options)| Option | Default | Meaning |
|---|---|---|
code |
required, except for localhost |
Pairing code from the Mac app. |
host |
the host serving the page | Address or name of the Mac, e.g. 192.168.1.20 or MecBook-Pro.local. |
port |
4321 (4322 with secure) |
The port shown on the Phone page. |
secure |
false |
Use https, see Presentations served over https. |
It throws if code is missing and the host is not localhost.
| Method | Does |
|---|---|
connect() |
Starts following the Mac. Reconnects by itself if the network drops. Returns the client. |
disconnect() |
Stops following. |
start() |
Tells the Mac to start listening. Returns a promise. |
stop() |
Tells the Mac to stop listening. Returns a promise. |
toggle() |
Starts or stops, depending on what the Mac is doing. |
on(event, fn) |
Subscribes to an event. Returns a function that unsubscribes. |
start, stop and toggle reject if the Mac cannot be reached or refuses the code, so wrap them in try / catch if a failure should not stop your page.
| Property | Value |
|---|---|
state |
Current pace state, see below. |
rate |
Smoothed rate in syllables per second, or null. |
monitoring |
true while the Mac is listening. |
connected |
true while the connection to the Mac is open. |
current |
The latest tick, or null. |
| Event | Fires when | Value |
|---|---|---|
tick |
Every 100 ms while the Mac is listening. | the reading, see below |
state |
The pace state changes. | the new state |
fast, warn, ok, listening, idle |
The pace enters that state. | the state |
status |
The Mac starts or stops listening, including when someone presses its Stop button. | { monitoring, state, profile } |
connection |
The connection opens or closes. | true or false |
error |
The Mac refuses the code. | { type: 'auth' } or { type: 'locked' } |
| State | Meaning |
|---|---|
idle |
The Mac is not listening, or it hears nothing. |
listening |
Listening, still warming up. |
ok |
At or below your baseline pace. |
warn |
Picking up, between the baseline and the too-fast line. |
fast |
Faster than your target for long enough to count. |
How fast is fast depends on the profile set in the Mac app (conversation or presentation) and on your calibration. The SDK reports what the app decided; it does not recalculate it.
tick reading{
smoothed: 3.4, // the number the gauge shows, syllables per second
rate: 3.6, // instantaneous rate, may be null when there is no speech
state: 'ok',
speaking: true, // someone is talking right now
level: 0.42, // microphone level, 0 to 1, handy for a VU meter
syllables: 83, // syllables counted this session
speakingMs: 16080, // time spent speaking this session
thresholds: { baseline: 3.3, warnAt: 3.6, fastAt: 4.0 }, // so you can draw your own gauge
t: 35200 // milliseconds since listening started
}
thresholds are in syllables per second. They let you scale a gauge without hard-coding numbers.
tf.on('fast', () => document.body.classList.add('too-fast'));
tf.on('ok', () => document.body.classList.remove('too-fast'));
tf.on('warn', () => document.body.classList.remove('too-fast'));
Reveal.on('ready', () => tf.start().catch(console.warn));
Reveal.on('slidechanged', (e) => {
if (e.currentSlide.dataset.pace === 'off') tf.stop();
else if (!tf.monitoring) tf.start();
});
tf.on('tick', (t) => {
const { baseline, fastAt } = t.thresholds;
const share = Math.min(1, (t.smoothed ?? 0) / (fastAt * 1.5));
bar.style.width = `${share * 100}%`;
bar.style.background = t.state === 'fast' ? 'crimson' : t.state === 'warn' ? 'orange' : 'seagreen';
});
tf.on('connection', (ok) => status.textContent = ok ? 'Connected' : 'Looking for the Mac…');
tf.on('error', (e) => status.textContent =
e.type === 'auth' ? 'Wrong pairing code' : 'Too many tries, wait a minute');
const { TooFast } = require('./toofast.js');
A page loaded over https (a hosted deck, for example) is not allowed to call plain http addresses. Use the Mac's secure address instead. It is the port after the normal one, with a certificate made on the Mac:
const tf = new TooFast({ host: '192.168.1.20', secure: true, code: '123456' });
Before the first use, open https://192.168.1.20:4322/ once in the same browser and accept the certificate warning. Pages opened from a local file or from http do not need any of this.
error with auth. The code is wrong, or it was changed. Read it again from the Phone page. If you are on the same Mac and loaded the page from a website or a file:// address, host it from localhost instead, or use the code.tick events. The Mac is not listening. Press Start in the Mac app, or call tf.start(). The status event tells you when it starts.