A lightweight, fast Command Line Interface (CLI) written in Go to convert text into speech using multiple cloud TTS providers. Currently supports Google Cloud, with planned support for AWS, Azure, IBM Cloud, and Alibaba Cloud.
This tool allows you to easily synthesize speech, save it to an MP3 file, or play it directly from your terminal on macOS, Linux, and Windows.
ttscli makes no network calls except to the TTS provider you configure (e.g., Google Cloud Text-to-Speech). No telemetry, no analytics, no phone-home. Your API key never leaves your machine except in requests to the provider's API endpoint.
- Go: Install Go
1.25+(matchesgo.mod). - Cloud Provider API Key: You need an API key for your chosen TTS provider.
- Google Cloud: Go to Google Cloud Console > APIs & Services > Credentials. Create an API Key and restrict it to the "Cloud Text-to-Speech API".
- Other providers: Support coming soon.
- Audio Player (Linux and Windows): The
speakcommand plays MP3 audio and needs an external player. macOS ships withafplayby default, so no install is needed there.- Linux: install one of
mpg123,paplay, orffplay.- Ubuntu/Debian:
sudo apt install mpg123
- Ubuntu/Debian:
- Windows: install one of
mpg123orffplay(ships with ffmpeg) and make sure the binary is on yourPath.- Scoop:
scoop install ffmpegorscoop install mpg123 - Chocolatey:
choco install ffmpegorchoco install mpg123
- Scoop:
- Linux: install one of
- Staticcheck (for local quality checks): Optional but recommended for contributors.
- Install:
go install honnef.co/go/tools/cmd/staticcheck@v0.7.0
- Install:
-
Install the CLI:
go install github.com/ppikrorngarn/ttscli/cmd/ttscli@latest
This installs the
ttsclibinary into your Go bin directory. IfGOBINis set, Go installs there. Otherwise it uses:$(go env GOPATH)/binYou can inspect
GOBINwith:$(go env GOBIN)Make sure that directory is in your
PATH.Use the same Go bin directory in your
PATH. IfGOBINis empty, that usually means$(go env GOPATH)/bin.Temporary shell setup for the current session:
export PATH="<go-bin-dir>:$PATH"
To make that permanent on macOS/Linux, add the same line to your shell profile such as
~/.zshrc,~/.bashrc, or~/.profile.Temporary PowerShell example for the current session:
$env:Path += ";<go-bin-dir>"
To make that permanent on Windows, add the directory to your user
Pathenvironment variable in System Settings, or persist it in your PowerShell profile.Then verify the install:
ttscli --version
-
Or download a pre-built binary from GitHub Releases:
Each tagged release (matching
v*.*.*) produces archives built by GoReleaser. The following combinations are published:OS Architectures Archive format Linux amd64, arm64 .tar.gzmacOS amd64, arm64 .tar.gzWindows amd64, arm64 .zipA
checksums.txtfile is published alongside each release for verification. Extract the archive, place thettsclibinary somewhere on yourPATH, and verify withttscli --version. -
Or clone the repository and build the binary locally: You can use the provided Makefile to build the project easily:
make build
(Or run
go build -o ttscli ./cmd/ttsclidirectly).If you want to install that locally built binary into your personal bin directory:
mkdir -p ~/.local/bin install -m 0755 ttscli ~/.local/bin/ttscli export PATH="$HOME/.local/bin:$PATH"
The
export PATH=...line only affects the current shell session. To make it permanent on macOS/Linux, add it to your shell profile such as~/.zshrc,~/.bashrc, or~/.profile.Windows PowerShell example:
New-Item -ItemType Directory -Force "$HOME\bin" | Out-Null Copy-Item .\ttscli.exe "$HOME\bin\ttscli.exe" $env:Path += ";" + "$HOME\bin"
The
$env:Path += ...line only affects the current PowerShell session. To make it permanent on Windows, add%USERPROFILE%\binto your userPathenvironment variable in System Settings, or persist it in your PowerShell profile.Then verify it:
ttscli --version
-
Run first-time setup (recommended):
ttscli setup
This guided flow prompts for API key, default language, and default voice, validates them, and can run a sound check. Press Enter on language/voice prompts to use built-in defaults:
en-USanden-US-Neural2-F. Note: The API key input is masked (hidden) as you type or paste for security.
For local quality checks:
make tools
export PATH="<go-bin-dir>:$PATH"
make checkUse the same Go bin directory that go install writes to.
If GOBIN is empty, that usually means $(go env GOPATH)/bin.
The export PATH=... line above only affects the current shell session.
If you use these tools often, add the same line to your shell profile.
For contribution and licensing details, see:
The Makefile provides the following targets:
| Target | Description |
|---|---|
make build |
Build the ttscli binary with version metadata injected via ldflags |
make clean |
Remove the compiled binary and any *.mp3 files |
make run |
Build and run the CLI (pass flags via ARGS="...") |
make test |
Run all tests with verbose output |
make test-race |
Run all tests with the race detector |
make tools |
Install local developer tools (staticcheck) |
make lint |
Run static analysis (staticcheck) |
make check |
Run go vet, tests, race tests, and lint |
make help |
Print all available targets with their descriptions |
Quick examples:
make build
make run ARGS="--version"
make checkIf you installed via go install, run ttscli ... from your shell PATH.
If you built locally with make build, run ./ttscli ... from the repo root.
If you manually copied the binary into a personal bin directory such as ~/.local/bin, run ttscli ... after adding that directory to your PATH.
On Windows, the equivalent is typically ttscli.exe from a directory such as %USERPROFILE%\bin after adding it to Path.
ttscli uses a profile-based configuration system that allows you to manage multiple TTS provider configurations. Each profile contains:
- Provider: The TTS service provider (e.g.,
gcp,aws,azure) - Name: A unique name for the profile (e.g.,
default,work,personal) - Credentials: Provider-specific authentication (e.g., API keys)
- Defaults: Default language and voice settings for that profile
Profile Key Format: {provider}:{name} (e.g., gcp:default, aws:work)
Profile Resolution:
--profileflag- Active profile from config
- Error if no active profile is set
Current Provider Support:
- β Google Cloud (GCP): Fully implemented
- π§ AWS Polly: Planned
- π§ Azure Speech: Planned
- π§ IBM Watson: Planned
- π§ Alibaba Cloud: Planned
ttscli stores its profiles in a JSON file. The CLI resolves the config path in two steps:
- Next to the binary β if a
config.jsonsits in the same directory as thettscliexecutable, it takes priority. Useful for portable or pinned setups. - User config directory (fallback):
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/ttscli/config.json |
| Linux | ~/.config/ttscli/config.json |
| Windows | %AppData%\ttscli\config.json |
Config file format:
{
"activeProvider": "gcp",
"activeProfile": "default",
"profiles": {
"gcp:default": {
"provider": "gcp",
"name": "default",
"credentials": {
"apiKey": "YOUR_API_KEY"
},
"defaults": {
"lang": "en-US",
"voice": "en-US-Neural2-F"
}
}
}
}Prefer managing profiles via the ttscli profile subcommands (see Command Reference) rather than editing this file by hand.
ttscli speak: Synthesize text to speech and play it immediately.ttscli save: Synthesize text to speech and save MP3 output.ttscli voices: List available voices (optionally filter with--lang).ttscli setup: Interactive first-run setup (creates a GCP profile).ttscli doctor: Health checks (config, profiles, API connectivity, playback). Returns non-zero when checks fail.ttscli completion <bash|zsh|fish>: Prints shell completion script to stdout.ttscli profile list: List all configured profiles.ttscli profile create: Create a new profile with provider credentials.ttscli profile delete <provider:name>: Delete a profile.ttscli profile use <provider:name>: Set the active profile.ttscli profile get <provider:name>: Show profile details.ttscli --version: Print build metadata.ttscli --help: Show top-level help.
| Flag | Type | Default | Notes |
|---|---|---|---|
--text |
string | "" |
Required for speak. Alias: -t. |
--lang |
string | en-US |
Language code for speak (or profile default). Alias: -l. |
--voice |
string | en-US-Neural2-F |
Voice name for synth (or profile default). Alias: -v. |
--profile |
string | "" |
Profile to use (e.g., gcp:default). Alias: -p. |
| Flag | Type | Default | Notes |
|---|---|---|---|
--text |
string | "" |
Required for save. Alias: -t. |
--out |
string | "" |
Required output MP3 path. Alias: -o. |
--lang |
string | en-US |
Language code for save (or profile default). Alias: -l. |
--voice |
string | en-US-Neural2-F |
Voice name for save (or profile default). Alias: -v. |
--profile |
string | "" |
Profile to use (e.g., gcp:default). Alias: -p. |
| Flag | Type | Default | Notes |
|---|---|---|---|
--lang |
string | en-US |
Optional language filter for voice listing. Alias: -l. |
--profile |
string | "" |
Profile to use (e.g., gcp:default). Alias: -p. |
| Flag | Type | Default | Notes |
|---|---|---|---|
--provider |
string | "" |
Required. Provider name (e.g., gcp). Alias: -P. |
--name |
string | "" |
Required. Unique profile name (e.g., default, work). Alias: -n. |
--api-key |
string | "" |
Required. API key for the provider. Alias: -k. |
--voice |
string | "" |
Optional default voice to seed into the profile. Alias: -v. |
0. Show CLI version/build metadata:
ttscli --version1. Run setup wizard (first run):
ttscli setup2. Run diagnostics:
ttscli doctor3. Manage profiles:
# List all profiles
ttscli profile list
# Create a new profile
ttscli profile create --provider gcp --name work --api-key YOUR_API_KEY
# Create a profile with a default voice already set
ttscli profile create --provider gcp --name work --api-key YOUR_API_KEY --voice en-US-Neural2-F
# Set active profile
ttscli profile use gcp:work
# View profile details
ttscli profile get gcp:work
# Delete a profile
ttscli profile delete gcp:work4. Generate shell completions:
ttscli completion zsh5. Play audio immediately (without saving):
# Using active profile
ttscli speak --text "Hello world, this is a test."
# Using specific profile
ttscli speak --text "Hello world, this is a test." --profile gcp:work6. Save audio to a file:
# Using active profile
ttscli save --text "Save this to a file." --out output.mp3
# Using specific profile
ttscli save --text "Save this to a file." --out output.mp3 --profile gcp:work7. List voices:
# Using active profile
ttscli voices --lang en-GB
# Using specific profile
ttscli voices --lang en-GB --profile gcp:workttscli speak -t "Hello world, this is a test."
ttscli save -t "Save this to a file." -o output.mp3
ttscli voices -l en-GBBy default, the CLI uses a female US English voice (en-US-Neural2-F). You can customize this using the --lang and --voice flags.
Example: British Male Voice:
ttscli speak --text "Hello there, how are you doing today?" --lang "en-GB" --voice "en-GB-Neural2-B"Example: French Voice:
ttscli speak --text "Bonjour le monde" --lang "fr-FR" --voice "fr-FR-Neural2-A"You can list all available voices for a specific language directly from the API.
List US English voices (default):
ttscli voicesList voices for another language:
ttscli voices --lang en-GBModule path: github.com/ppikrorngarn/ttscli
The codebase is organized into a thin cmd/ entry point and focused internal/ packages:
cmd/ttscli/ Binary entry point. Injects version/commit/date via ldflags at build time.
internal/cli/ Argument parsing. Owns the Config struct and all mode/subcommand constants.
internal/app/ Command handlers (setup, doctor, profile, completion). Orchestrates the run
flow: parse args -> resolve profile -> create provider -> execute.
internal/config/ JSON config file I/O. Profile CRUD. Two-step path resolution (local next
to the binary, falling back to the user config directory).
internal/player/ Cross-platform audio playback. macOS: afplay. Linux: mpg123/paplay/ffplay.
Windows: mpg123 or ffplay (requires install).
internal/tts/ Provider interface, GCP HTTP client, voice listing, PrintVoices helper.
Packages under internal/ use small, package-level function variables (e.g. readFile, writeFile, lookupEnv) for dependency injection, so tests can stub the filesystem, environment, and HTTP calls without real I/O.