-
Notifications
You must be signed in to change notification settings - Fork 0
overlays
VoxCtrl displays a visual overlay while the microphone is active or while TTS is speaking. The overlay is a dedicated, borderless, transparent Tauri window (560×160 px, transparent background, always-on-top, click-through) that renders a Svelte component over whatever application is in focus.
The overlay is a separate Tauri window running the /overlay route. It is configured with the following properties:
- Transparent — The window background is fully transparent; only the component's drawn pixels are visible.
- Always-on-Top — Floats persistently above all other active desktop windows.
-
Click-Through (
pointer-events: none) — Mouse and keyboard events pass cleanly through to the window beneath it, preventing any focus interruption. - Non-Resizable, Skip Taskbar — Does not appear in the desktop panel/taskbar and cannot be manually resized.
The overlay is controlled by the show_overlay / hide_overlay IPC commands and is automatically shown when recording starts and hidden when transcription completes (provided ui.show_overlay is enabled).
The active style is determined by config.ui.overlay_style. The window switches styles instantly without restarting—it temporarily unmounts the visualizer and remounts the new template after a 25ms flush to clear the WebKit compositor's frame buffer, preventing graphical ghosts on platforms like Linux (WebKitGTK/Wayland).
The visual presentation overlay window can be positioned dynamically on the active monitor where your mouse cursor or focused application is located.
Users can configure the screen alignment under Settings -> Visual & Feedback -> Overlay position or manually in config.json via ui.overlay_position.
Available screen positions:
-
"center"(Default) — Positions the overlay at the exact horizontal and vertical center of the active monitor. -
"top"— Positions the overlay at the horizontal center, aligned60logical pixels from the top of the monitor. -
"bottom"— Positions the overlay at the horizontal center, aligned60logical pixels from the bottom of the monitor.
Tauri dynamically calculates physical pixel values taking into account your display's current high-DPI scaling factor (scale_factor), ensuring perfect resolution-independent positioning on 1080p, 1440p, or 4K monitors. Changes to the position setting are instantly applied in real-time if the overlay window is currently visible.
In multi-monitor setups, VoxCtrl allows you to specify exactly which display screen the visual overlay should appear on.
You can configure the target display under Settings -> Visual & Feedback -> Overlay display or manually in config.json via ui.overlay_monitor.
Options:
-
"primary"(Default) — Constrains the overlay to the OS-defined primary display screen. -
Specific Monitor Name (e.g.
"HDMI-1","DP-2") — Connected displays are dynamically queried once at application startup. Selecting one of these binds the visualizer to that specific panel.
If the configured target monitor is unplugged or disconnected at runtime, the Tauri backend will automatically fail over to the Primary Monitor to keep the visualizer fully accessible. When this happens, a golden warning badge will be displayed inside the settings UI to alert you that the target display is disconnected and fallback mode is active.
Four built-in styles are available. The default is Ocean Wave.
A layered animated ocean-wave visualization that reacts to real-time audio levels.
- Three overlapping SVG wave layers in deep blue, cyan-aqua, and ice-teal.
- Waves swell in amplitude in response to the
audio-levelevent stream from the Rust backend. - Displays a "Voice Activity" label and a green routing target badge inside a dark rounded card.
- Slides in with a subtle upward ease transition on appear.
A symmetric, capsule-shaped spectrum equalizer card.
- 45 vertical visualizer bars that respond to RMS audio level with a fast-attack, fast-release physics envelope.
- Displays the active routing target badge and HSL-gradient bars (purple-pink-rose).
- Built with a sleek dark-obsidian glassmorphism card.
A centre-weighted bar graph with a Gaussian amplitude envelope.
- 48 bars, tallest in the centre and tapering at the edges.
- Adds randomized per-bar noise during active recording for a premium spectrum-analyser look.
- Transitions to a flat idle glide state when not actively recording.
A minimalist target-tracking indicator.
- Three visual states: Initializing (amber, "Connecting Mic…"), Recording (orange, active target label), Processing (blue, "Processing…").
- Dual concentric SVG rings pulse and fade outward during active recording, scaling in glow intensity relative to voice amplitude.
VoxCtrl supports user-creatable, customizable visual overlays loaded dynamically from a local directory at runtime. You can build visualizers using plain HTML templates, vanilla CSS styling, and standard JavaScript, with high-performance access to the real-time audio stream.
Custom overlays are scanned dynamically at launch from your local share directory:
-
Linux:
~/.local/share/voxctrl/overlays/ -
macOS:
~/Library/Application Support/ai.voxctrl.app/overlays/ -
Windows:
AppData\Local\ai.voxctrl.app\overlays\
To create your own overlay, create a new subfolder under overlays/. The folder name serves as the overlay's ID. Inside the folder, place two files:
overlays/
└── my-visualizer/
├── index.html <-- Overlay structure & JS animations
└── style.css <-- Overlay styling & layouts
Note
Bundled Example: VoxCtrl automatically bundles and extracts a premium gradient-wave custom overlay folder inside your local directory at launch. Use this folder as a live reference!
Your index.html template can include HTML layouts, inline SVG vectors, and local scripts. VoxCtrl automatically scans and replaces the following reactive placeholders at runtime:
-
{{trigger}}— Replaced with the active global hotkey trigger label (e.g.Meta + Space). -
{{target}}— Replaced with the active target delivery name (e.g.Focused Window).
VoxCtrl binds high-speed, reactive CSS custom properties directly to the .overlay-root container at 60fps. You can use these variables inside your style.css to build pure, GPU-accelerated CSS animations:
| CSS Custom Variable | Value Range | Purpose |
|---|---|---|
--voxctrl-audio-level |
0.0 to 1.0
|
Real-time interpolated microphone amplitude level. |
--voxctrl-recording |
0 or 1
|
Indication if the microphone is actively recording. |
--voxctrl-processing |
0 or 1
|
Indication if the backend is actively performing inference/thinking. |
--voxctrl-speaking |
0 or 1
|
Indication if the Piper neural TTS voice is speaking. |
--voxctrl-audio-ready |
0 or 1
|
Indication if the audio system is fully initialized. |
.indicator {
/* Scale element size dynamically on microphone volume level */
transform: scale(calc(1.0 + var(--voxctrl-audio-level) * 0.5));
/* Transition color based on recording status */
background: color-mix(in srgb, #ff6b35 calc(var(--voxctrl-recording) * 100%), #38bdf8);
}For advanced visualizers using HTML5 <canvas>, WebGL, or SVG path morphing, standard vanilla custom DOM events are dispatched on the window object:
Dispatched in real-time when fresh amplitude inputs are received.
-
Payload (
event.detail):number(raw RMS audio energy, roughly0.0to1.0).
window.addEventListener("voxctrl-audio-level", (e) => {
const rawVolume = e.detail;
// Drive custom visual animations
});Dispatched at 60fps containing a full state object.
-
Payload (
event.detail):{ "recording": true, "processing": false, "speaking": false, "audio_ready": true, "active_target_label": "Focused Window", "audio_level": 0.45 }
window.addEventListener("voxctrl-status", (e) => {
const { recording, audio_level, active_target_label } = e.detail;
if (recording) {
// update canvas loop
}
});Svelte's standard template injector blocks <script> blocks for security reasons. To make user-creatable visualizers fully programmable, VoxCtrl runs a custom action on mount:
- It extracts all script blocks inside your
index.html. - Re-compiles them dynamically.
- Re-injects them into the DOM frame to force secure execution.
This allows you to write high-performance loops (such as requestAnimationFrame render cycles) inside standard <script> tags natively, without requiring webpack, node_modules, or external bundlers.
To prevent custom layouts from overriding or breaking the core built-in application visualizers, the scanning system implements automatic conflict resolution:
- Folder names matching built-in styles (
waveform,pulse,blue_wave,voice_card,none) are reserved. - If a user names their folder exactly
pulse, the scanner automatically registers it aspulse_customin the styles dropdown. This ensures both visualizers can coexist and run flawlessly.
Below is a complete, copy-pasteable example of a custom visualizer card featuring a glowing circular audio-reactive indicator.
Create a folder inside your local directory named glow-ring:
mkdir -p ~/.local/share/voxctrl/overlays/glow-ringSave this file as ~/.local/share/voxctrl/overlays/glow-ring/index.html:
<div class="custom-card">
<div class="meta-row">
<span class="title">My Glow HUD</span>
<span class="target-badge">{{target}}</span>
</div>
<div class="visualizer-container">
<div class="glow-circle" id="myGlowCircle"></div>
</div>
<script>
let currentVolume = 0;
let targetVolume = 0;
window.addEventListener("voxctrl-audio-level", (e) => {
// Scale level up for visual display
targetVolume = Math.min(1.0, e.detail * 100.0);
});
function draw() {
// Smooth linear interpolation for visuals
currentVolume += (targetVolume - currentVolume) * 0.3;
targetVolume *= 0.85;
const circle = document.getElementById("myGlowCircle");
if (circle) {
// Expand circle and increase glow drop-shadow dynamically
const scale = 1.0 + currentVolume * 1.5;
const shadow = currentVolume * 30;
circle.style.transform = `scale(${scale})`;
circle.style.boxShadow = `0 0 ${20 + shadow}px rgba(56, 189, 248, ${0.4 + currentVolume * 0.6})`;
}
requestAnimationFrame(draw);
}
draw();
</script>
</div>Save this file as ~/.local/share/voxctrl/overlays/glow-ring/style.css:
.custom-card {
width: 300px;
height: 110px;
background: rgba(13, 13, 16, 0.93);
border: 1.2px solid rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 12px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.7);
backdrop-filter: blur(12px);
}
.meta-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
font-family: sans-serif;
font-size: 11px;
color: rgba(255, 255, 255, 0.3);
font-weight: 700;
text-transform: uppercase;
}
.target-badge {
font-family: sans-serif;
font-size: 10px;
color: #38bdf8;
background: rgba(56, 189, 248, 0.1);
border: 1px solid rgba(56, 189, 248, 0.3);
border-radius: 4px;
padding: 2px 6px;
font-weight: 600;
}
.visualizer-container {
display: flex;
align-items: center;
justify-content: center;
height: 60px;
width: 100%;
}
.glow-circle {
width: 16px;
height: 16px;
border-radius: 50%;
background: #38bdf8;
transition: transform 0.05s ease-out;
}- Open VoxCtrl Settings -> Visual & Feedback -> Overlay style dropdown.
- Select your newly registered
"glow-ring"overlay! - Trigger dictation and watch your custom glow visualizer react smoothly to your voice.