1. Create the face
In Multimeter’s Settings, click Create Face… (Option-Command-N) and name it Dot. Multimeter makes a folder of starter files and shows it in Finder. Open the folder in your editor. Multimeter reloads the face every time you save.
2. The markup
Replace index.html with a dot and a value:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<span id="dot"></span>
<span id="value">--</span>
<script src="meter.js"></script>
</body>
</html>3. The styles
A meter is 22 points tall and transparent. Use the system font and tabular numbers so digits don’t jiggle:
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
height: 22px; overflow: hidden; background: transparent;
display: flex; align-items: center; justify-content: center; gap: 4px;
font: 600 11px/1 -apple-system, BlinkMacSystemFont, sans-serif;
font-variant-numeric: tabular-nums;
color: white;
--dot: #30d158;
}
body.light-mode { color: black; }
body.warning { --dot: #ffd60a; }
body.critical { --dot: #ff453a; }
#dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dot); }Multimeter adds light-mode or dark-mode to body to match the menu bar, which follows the wallpaper, not only the system setting.
4. The script
Multimeter puts the data in window.meterData and fires meterUpdate whenever it changes. Draw once, draw on every update, then say you’re ready:
const dot = document.getElementById('dot');
const value = document.getElementById('value');
function render(d) {
value.textContent = d.available === false ? '--' : d.formatted;
document.body.classList.toggle('light-mode', d.colorScheme === 'light');
document.body.classList.toggle('warning', d.state === 'warning');
document.body.classList.toggle('critical', d.state === 'critical');
// The color picked in Customize, while nothing needs attention
if (d.tint && d.state === 'normal') document.body.style.setProperty('--dot', d.tint);
else document.body.style.removeProperty('--dot');
}
window.addEventListener('meterUpdate', (e) => render(e.detail));
if (window.meterData) render(window.meterData);
window.meterReady = true;formatted is already localized and includes the unit (“23%”, “1.2 MB/s”, “4:12”), so the same face works for every source. The full list of fields is on Creating Meters.
5. The manifest
{
"name": "Dot",
"description": "A colored dot and the value.",
"version": "1.0.0",
"author": "Your Name",
"apiVersion": 2,
"sources": ["any"],
"defaultSource": "cpu",
"width": 48,
"minWidth": 36,
"maxWidth": 80,
"supportsLightMode": true,
"supportsDarkMode": true
}"sources": ["any"] offers the face for every source. A battery-only face would say ["battery"].
6. Test it
- Choose Add Meter… and try your face on CPU, Network, a Timer and a Sent Value. Long values like 1.2 MB/s should still fit at
minWidth. - Set a low warning level in Customize This Meter… to see the yellow and red states.
- Switch your wallpaper between light and dark to check both menu bars.
- Errors from your JavaScript show up in Console.app.
Faster loop: open your index.html in Safari and feed it data from the Web Inspector console:
window.meterData = { formatted: '87%', level: 0.87, state: 'warning', colorScheme: 'dark', available: true }
window.dispatchEvent(new CustomEvent('meterUpdate', { detail: window.meterData }))7. Keep it light
Something we measured while building the built-in faces: a CSS transition on a value that updates often makes WebKit redraw the menu bar at the display’s full 120 Hz. A CPU Needle Gauge cost 7.5% CPU with a 0.9 second transition and 0.6% without. If your face animates a number that changes every few seconds, step it with a 30 frames-per-second timer instead, and skip animation when reduceMotion is true.
8. Share it
Compress the folder in Finder, rename the .zip to .multimeter, and submit it to the gallery. Anyone can add it with Import… (Command-O) or by double-clicking the file.
Check it worked
Your Dot face shows a green dot and the value in the menu bar, turns yellow and red at the alert levels, and reads correctly in light and dark menu bars.