Building a Real-Time Desktop HUD with Electron and Live Data
How I built a glanceable command center on a 3840x1100 bar display — pulling live data from miners, 3D printers, weather APIs, and system stats into a JARVIS-style ambient dashboard.
The idea
I wanted a display I could glance at and immediately know the state of everything — mining hashrates, 3D printer status, system thermals, weather, and more. Not a browser tab I’d forget about. Something always on, always visible, sitting right below my main monitor.
The answer was a 3840x1100 ultrawide bar display running a custom Electron app. I call it the Batcave Deck.
The hardware
The bar display mounts directly below my primary monitor, spanning the full width of my desk. At 3840x1100 resolution, it’s wide enough to show 4-5 data panels side by side without scrolling. Think of it as a permanent heads-up display for your workstation.
The display connects via HDMI to my main PC and runs as an extended desktop. Electron launches fullscreen on that display at boot, so it’s always on and always showing live data.
The stack
The app is built with Electron and vanilla JavaScript — no React, no build tools, just modules. The architecture is intentionally simple:
batcave-deck/
├── main.js # Electron main process
├── renderer/
│ ├── index.html # Shell layout
│ ├── app.js # Module loader + panel cycling
│ ├── modules/
│ │ ├── system.js # CPU, GPU, RAM, VRAM
│ │ ├── miners.js # Bitaxe hashrate + stats
│ │ ├── printers.js # 3D printer status
│ │ ├── weather.js # Local weather
│ │ ├── cameras.js # Print farm camera feeds
│ │ └── ai-chat.js # Gemini + Claude streaming
│ └── styles.css
└── config.json # API keys, endpoints, intervals
Each module exports an init() and update() function. The main app loop calls update() on each module at its configured interval — typically every 5-30 seconds depending on the data source.
Design language
The aesthetic is what I’d describe as NerdlyBuilds meets Iron Man JARVIS. Dark void background, bright accent data, and clean typography:
- Background: Deep navy void (
#050810) - Primary accent: NerdlyBuilds green (
#00e676) - Headers: Orbitron (that sci-fi geometric font)
- Data values: JetBrains Mono (monospace, clean numbers)
- Body text: Outfit (modern, readable)
The goal is zero cognitive load. Glance, absorb, move on. If something needs attention — a printer error, a miner going offline, high thermals — it should pop visually without me reading anything.
Pulling live data
System stats
The system module uses systeminformation to poll CPU usage, temperature, GPU utilization, RAM, and VRAM:
const si = require('systeminformation');
async function update() {
const [cpu, cpuTemp, mem, gpu] = await Promise.all([
si.currentLoad(),
si.cpuTemperature(),
si.mem(),
si.graphics()
]);
render({
cpuUsage: cpu.currentLoad.toFixed(0),
cpuTemp: cpuTemp.main,
ramUsed: (mem.used / 1073741824).toFixed(1),
gpuUsage: gpu.controllers[0]?.utilizationGpu,
vramUsed: (gpu.controllers[0]?.memoryUsed / 1024).toFixed(1)
});
}
This gives me real-time thermals and load at a glance. When I’m rendering in DaVinci Resolve or running an AI model, I can see the GPU peg without alt-tabbing to Task Manager.
Mining stats
I run a small fleet of Bitaxe miners on my local network. Each one exposes a REST API, so polling them is straightforward:
async function pollMiner(ip) {
const res = await fetch(`http://${ip}/api/system/info`);
const data = await res.json();
return {
hashRate: data.hashRate,
bestDiff: data.bestDiff,
temp: data.temp,
sharesAccepted: data.sharesAccepted
};
}
The Deck shows aggregate hashrate across all miners, individual temps, and the best difficulty share found. It’s not about profitability — it’s about the engineering and having real mining telemetry in your peripheral vision.
3D printer cameras
This was the trickiest integration. My Bambu printers expose RTSPS camera feeds, but Electron’s Chromium engine doesn’t handle RTSP natively. My first instinct was MJPEG streaming via fetch ReadableStream, but that only captures a single frame in Electron — the stream never advances.
The solution: a go2rtc Docker container on my server that proxies the RTSPS feeds and exposes HTTP snapshot endpoints. The Deck polls these snapshots every 500ms with a simple image refresh:
function startCameraFeed(url, imgElement) {
setInterval(() => {
const fresh = `${url}?t=${Date.now()}`;
const img = new Image();
img.onload = () => { imgElement.src = fresh; };
img.src = fresh;
}, 500);
}
Is it elegant? No. Does it work reliably in Electron where every other approach failed? Yes. The camera overlay uses a position:fixed div on document.body outside the main app container, so it layers cleanly over the dashboard panels.
Weather
OpenWeatherMap with a simple API call. One gotcha I ran into: on my network, the HTTPS endpoint times out but HTTP works fine. Sometimes the boring solution is the right one.
AI chat module
The Deck also has a built-in AI chat panel. It streams responses from both Gemini and Claude, so I can ask quick questions without leaving my current workflow. The responses render in the same HUD aesthetic — green accent text on the dark void background.
What’s next
I’m working on clipboard sync between machines, deeper integration with my home automation, and potentially adding stock/crypto tickers alongside the mining stats. The modular architecture makes it easy — write a new module, add it to the config, and it slots right into the layout.
The full source is structured so anyone with a bar display (or even a standard second monitor) could adapt it. The key insight isn’t the specific hardware — it’s that a dedicated ambient display running custom software fundamentally changes how you interact with your systems. You stop checking things and start noticing them.
The Batcave Deck is part of my ongoing command center project. Follow along on NerdlyBuilds for more builds.
