_ _ _ _ _ _ .-----------.
| | (_) | (_) | (_) .-' .-----. '-.
| |__ _| |__ _| | ___ / .--' '--. \
| '_ \| | '_ \| | |/ / | / / .-----. \ \
| | | | | |_) | | <| | / / / \ \ \
|_| |_|_|_.__/|_|_|\_\_| | | | (O) | | |
A macOS menu bar app that reads selected text aloud using OpenAI or ElevenLabs text-to-speech APIs. Built for agentic workflows, Hibiki supports global hotkeys, streaming audio playback, and a CLI (hibiki --text "Hello") so text from editors, browsers, and terminal sessions can be spoken instantly. Agent integration is a core feature: the repo includes ready-to-use skill and hook files so coding agents can trigger speech and spoken summaries directly from workflows. Hibiki also supports optional AI summarization and translation before playback, plus history and usage tracking.
Blog walkthrough: How I Built Hibiki
This is a fork of RobertTLange/hibiki by Robert T. Lange. All original credit goes to the upstream author. This fork adds a floating "Speak" button that appears next to highlighted text in any app, so you can read a selection aloud with a single click instead of only via the global hotkey — a "Wispr Flow in reverse" for text-to-speech output.
What's new in this fork:
- System-wide selection monitoring (
Sources/Hibiki/Selection/) using the Accessibility API with a non-destructive read that never touches the clipboard.- A floating speak/stop pill positioned next to the selection, with multi-monitor clamping.
- Settings to enable/disable the button, choose its position, set a minimum selection length, exclude specific apps, and launch at login.
| Feature | Description |
|---|---|
| Floating Speak Button | A pill appears next to highlighted text in any app; click to read it aloud (fork addition) |
| Agent Integration | Built-in skill + hook templates for agent-driven spoken output |
| Global Hotkeys | Option+F for TTS, Shift+Option+F for Summarize+TTS |
| Streaming Audio | Audio plays as it's generated for fast response |
| TTS Providers | OpenAI, ElevenLabs, or local Pocket TTS |
| AI Summarization | Condense long texts before reading (GPT-5 Nano/Mini/5.2) |
| Translation | Translate to English, Japanese, German, French, Spanish |
| CLI Tool | hibiki --text "Hello" or hibiki --file-name README.md with --summarize and --translate flags |
| History & Stats | Track past requests and API usage costs |
| Audio Player UI | Visual waveform display with playback speed control (1.0x-2.5x) |
- macOS 14.0 or later
- OpenAI API key (required for summarization/translation and OpenAI TTS)
- ElevenLabs API key (required if using ElevenLabs TTS provider)
uv(required for one-click managed Pocket TTS install)- Accessibility permission (to read selected text from other apps)
Build from source (recommended):
git clone https://github.com/RobertTLange/hibiki.git
cd hibiki
./build.sh
cp -R .build/Hibiki.app /Applications/Hibiki.app
open /Applications/Hibiki.app
sudo ln -sf /Applications/Hibiki.app/Contents/MacOS/hibiki-cli /usr/local/bin/hibikiCLI binary path inside the app bundle: Hibiki.app/Contents/MacOS/hibiki-cli
- Launch Hibiki — appears as speaker icon in menu bar
- Grant Accessibility Permission — Settings → follow instructions
- Add API key(s) — OpenAI and/or ElevenLabs in Settings, or env vars
OPENAI_API_KEY/ELEVENLABS_API_KEY - Configure Hotkeys (optional) — defaults: Option+F (TTS), Shift+Option+F (Summarize+TTS)
If Hibiki is launched from Finder or open, it may not inherit terminal shell exports.
Set env vars through launchctl for GUI app visibility:
launchctl setenv OPENAI_API_KEY "sk-..."
launchctl setenv ELEVENLABS_API_KEY "..."
launchctl setenv POCKET_TTS_BASE_URL "http://127.0.0.1:8000"Remove them later with launchctl unsetenv <NAME>.
- Open Settings → Configuration → Local Pocket TTS (Managed).
- Enable managed runtime.
- Click Install / Reinstall (uses
uvto create a local venv and installpocket-tts). - Click Start (or enable auto-start).
- Select provider Pocket TTS (Local) in the Text to Speech section.
Default managed endpoint: http://127.0.0.1:8000
Integration note: Hibiki uses a managed local Pocket TTS runtime (install/start/health checks in-app) and streams Pocket-generated WAV audio directly to the built-in player.
Official Pocket TTS repository: kyutai-labs/pocket-tts
Note: Pocket local mode is currently English-only in Hibiki.
The Hibiki app must be running. Use hotkeys (Option+F, Shift+Option+F) for GUI-based TTS.
hibiki --text "Hello, world!" # Basic TTS
hibiki --file-name README.md # Read from file
hibiki --text "Long article..." --summarize # Summarize + TTS
hibiki --file-name Sources/HibikiCLI/HibikiCLI.swift --summarize # Summarize file + TTS
hibiki --text "Long article..." --summarize --prompt "Summarize in 3 bullets." # Custom summary prompt
hibiki --text "Hello" --translate ja # Translate + TTS
hibiki --text "Article..." --summarize --translate fr # Full pipelineLanguages: en (English), ja (Japanese), de (German), fr (French), es (Spanish)
Prompt override: --prompt replaces the default summarization prompt (requires --summarize).
Input source: use exactly one of --text or --file-name.
File decoding: --file-name expects UTF-8 text files.
Markdown cleanup: .md / .markdown files get balanced cleanup (frontmatter/comments removed, headings/lists normalized, links simplified, code fences converted for speech).
Input size limit: request URL payload must stay under ~32KB after encoding; very large files should be summarized or split.
See skills/hibiki-tts/ for integration files.
Add Hibiki as a Claude Code skill so Claude can speak text aloud:
mkdir -p ~/.claude/skills/tts-hibiki
cp skills/hibiki-tts/SKILL.md ~/.claude/skills/tts-hibiki/SKILL.mdInstall directly via npx:
TARGET_DIR="$HOME/.claude/skills/tts-hibiki" npx --yes zx -e 'const fs=require("node:fs/promises"),path=require("node:path");const target=process.env.TARGET_DIR;await fs.mkdir(target,{recursive:true});const res=await fetch("https://raw.githubusercontent.com/RobertTLange/hibiki/main/skills/hibiki-tts/SKILL.md");if(!res.ok)throw new Error(`Failed to download SKILL.md: ${res.status}`);await fs.writeFile(path.join(target,"SKILL.md"),await res.text(),"utf8")'Automatically speak Claude's final response when a session ends:
cp skills/hibiki-tts/speak-summary.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/speak-summary.shThen merge skills/hibiki-tts/hooks.json into your ~/.claude/settings.json.
- Never commit API keys to this repo.
- Prefer configuring keys inside Hibiki Settings.
- For menu bar app launches, prefer
launchctl setenv ...so Hibiki can read env vars.
| Issue | Solution |
|---|---|
| "No text selected" | Ensure text is selected before pressing hotkey |
| "Accessibility permission not granted" | Grant permission in System Settings |
| "No OpenAI API key configured" | Add key in Settings, or run launchctl setenv OPENAI_API_KEY "sk-..." |
| "No ElevenLabs API key configured" | Add key in Settings, or run launchctl setenv ELEVENLABS_API_KEY "..." |
| "uv was not found" | Install uv and retry Pocket managed install |
| Chrome not capturing text | Hibiki auto-falls back to clipboard (Cmd+C) |
Debug logs available in Settings → Debug tab.
Sources/
├── HibikiCLI/
│ └── HibikiCLI.swift # CLI executable (argument parsing, URL scheme)
└── Hibiki/
├── HibikiApp.swift # App entry point
├── AppDelegate.swift # Menu bar setup, window management, URL handling
├── Selection/ # Floating speak button (fork addition)
│ ├── SelectionMonitor.swift # System-wide selection detection (AX + polling)
│ ├── SelectionHUDController.swift # Floating panel lifecycle + positioning
│ └── SelectionHUDView.swift # The speak/stop pill UI
├── Core/
│ ├── AppState.swift # Main application state
│ ├── AccessibilityManager.swift # Text selection via accessibility API
│ ├── LaunchAtLogin.swift # Launch-at-login via SMAppService (fork addition)
│ ├── CLIRequestHandler.swift # Handles CLI requests via URL scheme
│ ├── PocketTTSRuntimeManager.swift # Managed local Pocket runtime install/start/health
│ ├── PermissionManager.swift # Permission checking
│ ├── DebugLogger.swift # In-app debug logging
│ ├── HistoryManager.swift # TTS history tracking
│ ├── HistoryEntry.swift # History data model
│ ├── LLMService.swift # OpenAI LLM API client (summarization/translation)
│ ├── TextChunker.swift # Long text chunking
│ └── UsageStatistics.swift # Usage tracking
├── Audio/
│ ├── TTSService.swift # OpenAI + ElevenLabs TTS API client
│ ├── WAVStreamDecoder.swift # Streaming WAV -> PCM decoder for local Pocket TTS
│ ├── StreamingAudioPlayer.swift # PCM audio playback
│ └── AudioLevelMonitor.swift # Audio level monitoring for waveform
└── Views/
├── MainSettingsView.swift # Tabbed settings window
├── MenuBarView.swift # Menu bar popover
├── AudioPlayerPanel.swift # Audio player with waveform
├── WaveformView.swift # Waveform visualization
└── Tabs/
├── ConfigurationTab.swift # Provider, API keys, voice, hotkeys
├── DebugTab.swift # Debug log viewer
├── HistoryTab.swift # TTS history
└── StatisticsTab.swift # Usage statistics
