Text-to-speech has a genuinely free, local, standard option already, SpeechSynthesis,
the output half of the Web Speech API, and it’s worth trying first for anything simple:
it needs no server and generally works offline once a browser has its voices loaded.
Piper exists for when you need more than that, a specific voice, better prosody, or
speech generated server-side rather than only in a visitor’s browser. This lesson gets
Piper running locally and calls it from WordPress. It also flags something that
actually matters for anyone shipping this in a real product: the original project was
archived, and its actively maintained successor changed license.
Lesson 3’s pattern of running a local Python service and calling it from WordPress
over wp_remote_post(), this lesson repeats that shape for the opposite direction,
text in, audio out.
Step 1: Piper’s real, current status, verified directly
The original rhasspy/piper repository was archived by its owner on October 6, 2025,
and is now read-only. It was MIT-licensed. Active development did not stop, it moved
to a new repository, OHF-Voice/piper1-gpl, maintained by the Open Home Foundation’s
Voice team, and that successor is licensed under GPL-3.0, not MIT. The installable
package from the maintained project is piper-tts on PyPI. Before following any older
tutorial that points at the archived repository’s build instructions, confirm you’re
installing the current package, since the setup steps changed along with the license.
That license change is worth sitting with for a moment before writing any code. MIT let you bundle Piper into a closed-source commercial product with no obligation to share your own source. GPL-3.0 carries real reciprocal terms, if you distribute a product that includes GPL-3.0-licensed code, you generally need to make that product’s source available under a compatible license too. Whether that’s a problem depends entirely on how you ship: a WordPress plugin already released under the GPL, which most are, has no new conflict here. A closed-source SaaS product bundling Piper for distribution is a different situation, and worth a real conversation with whoever handles licensing for that project, not a decision to make silently in a code review.
Step 2: Install the current, maintained package
# Context: on the machine that will run text-to-speech, can be the same host as Lesson 3's whisper-server
python3 -m venv piper-env
source piper-env/bin/activate
pip install piper-tts fastapi uvicorn
Then download a voice model, Piper’s voices are separate .onnx files distributed
alongside the project:
python3 -m piper.download_voices en_US-lessac-medium
Step 3: Confirm it works from the command line first
echo "This is a test of local text to speech." | piper \
--model en_US-lessac-medium \
--output_file test-output.wav
Play test-output.wav. Hearing an actual synthesized voice, not an error, confirms
the model downloaded correctly and the package is runnable before wiring any
WordPress code around it.
Step 4: Wrap it as a small local HTTP service
# File: piper-server/main.py
from fastapi import FastAPI
from fastapi.responses import Response
from piper import PiperVoice
import io
import wave
app = FastAPI()
voice = PiperVoice.load( "en_US-lessac-medium.onnx" )
@app.post( "/speak" )
async def speak( payload: dict ):
text = payload.get( "text", "" )
buffer = io.BytesIO()
with wave.open( buffer, "wb" ) as wav_file:
voice.synthesize( text, wav_file )
return Response( content=buffer.getvalue(), media_type="audio/wav" )
# Context: starting the local text-to-speech service on port 5002
uvicorn main:app --host 127.0.0.1 --port 5002
Step 5: Call it from WordPress
// File: my-plugin/class-local-tts.php
function my_plugin_speak_text( string $text ): string|WP_Error {
$response = wp_remote_post(
'http://127.0.0.1:5002/speak',
array(
'timeout' => 30,
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array( 'text' => $text ) ),
)
);
if ( is_wp_error( $response ) ) {
return $response;
}
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
return new WP_Error( 'tts_failed', 'Local text-to-speech request failed.' );
}
$upload_dir = wp_upload_dir();
$filename = 'tts-' . wp_generate_password( 8, false ) . '.wav';
$file_path = trailingslashit( $upload_dir['path'] ) . $filename;
file_put_contents( $file_path, wp_remote_retrieve_body( $response ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
return trailingslashit( $upload_dir['url'] ) . $filename;
}
This writes the returned WAV audio into the uploads directory and hands back a real, playable URL, the shape Lesson 5’s widget expects when it plays a spoken reply back to a visitor.
Every call to my_plugin_speak_text() writes a new file into uploads. Left unchecked,
that grows without bound. A scheduled cleanup, deleting files under this pattern older
than a day, is a real production requirement, not an optional polish step, add it
before this ships anywhere with real traffic.
Test it: generate and play a real reply
wp eval '
$url = my_plugin_speak_text( "This is a test of the local text to speech pipeline." );
echo is_wp_error( $url ) ? $url->get_error_message() : $url;
'
Open the returned URL directly in a browser tab and confirm you actually hear the sentence spoken back, not silence or a corrupted file.
Recap
SpeechSynthesis, the browser’s own built-in text-to-speech, is worth trying first
since it needs nothing installed. Piper is the real, local, neural alternative for
when a specific voice or server-side generation matters, run as a small local HTTP
service and called with the same wp_remote_post() pattern this course has used
throughout. The one detail that genuinely changes how you ship this: the original
MIT-licensed rhasspy/piper repository is archived, the maintained project now lives
at OHF-Voice/piper1-gpl under GPL-3.0, and that license’s reciprocal terms are worth
a real decision, not an assumption, before bundling it into a distributed product.
Resources & further reading
- OHF-Voice/piper1-gpl repository, GitHub, the actively maintained successor
- rhasspy/piper repository (archived), GitHub
- SpeechSynthesis interface, MDN Web Docs, developer.mozilla.org