Model or dataset
dbccccccc/ttsfm avatar
dbccccccc/ttsfm

TTSFM: an OpenAI-compatible TTS endpoint built on a reverse-engineered openai.fm backend

TTSFM mirrors OpenAI's TTS service, providing a compatible interface for text-to-speech conversion with multiple voice options for free.

736 stars114 forksPythonMIT

At a glance

What is it?
TTSFM wraps the openai.fm service in a FastAPI-style server with an OpenAI-shaped /v1/audio/speech route, an 11-voice catalogue and a Docker image split that decides which audio features you actually get. It is an educational reverse-engineering project, not a production TTS vendor.
Who is it for?
Adopt TTSFM if you want a local endpoint that speaks the OpenAI /v1/audio/speech request shape and you accept that the audio ultimately comes from someone else's reverse-engineered service. Do not adopt it for a paid product, for anything requiring an uptime or content guarantee, or where you cannot accept the README's own educational-and-research restriction.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 77 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap TTSFM fills, and the one it does not

OpenAI's speech endpoint has a stable request shape: model, input, voice, response_format, and an optional speed. Any code written against it is short and easy to copy. The problem TTSFM addresses is what happens when you want that same call to work without an OpenAI key, or when you want to experiment with voices and formats locally before spending anything. The README describes the project as a free, OpenAI-compatible text-to-speech API service built on top of the openai.fm backend, with a Python SDK, REST endpoints and a web playground. The intended audience is developers who want a drop-in replacement for the OpenAI TTS call while testing, plus anyone who wants a browser page to hear the voices without writing code first.

The second half of that sentence matters more than the first. TTSFM does not synthesise speech. It is a client and a server in front of openai.fm, and the README is explicit that it is a reverse-engineered implementation of that service, intended for educational and research purposes only, not for commercial use or production. So the honest description of the problem solved is narrower than the tagline suggests: it removes the credential requirement and the boilerplate, not the dependency on an upstream service you do not control.

Request path: from your curl call to a combined audio file

The architecture visible in the material is a thin proxy with local post-processing. A request arrives at the container on /v1/audio/speech with an OpenAI-shaped JSON body. The server forwards the text and voice selection to the openai.fm backend, receives audio, and then, if the request needs it, applies local work before returning bytes to the caller. The docs directory includes an architecture overview at docs/architecture.md with component diagrams, which is where the exact internal boundaries are documented; the README itself only describes the outer behaviour.

Two pieces of that outer behaviour are worth naming. The first is long-text handling: the README states that content of any length is supported through automatic text splitting and audio combining. That means the server chunks your input, issues multiple upstream requests, and concatenates the resulting audio. The second is speed: adjustments between 0.25x and 4.0x are performed with ffmpeg-based audio processing rather than by asking the upstream service for a different rate. Both of these are local operations, and both are conditional on the image variant you deployed, which is the single most important structural fact about this project.

The full and slim images are two different products

TTSFM ships two Docker images, and the feature matrix between them is not a minor packaging detail. The full variant is documented as including ffmpeg and therefore all six audio formats (MP3, WAV, OPUS, AAC, FLAC, PCM), speed adjustment across the full 0.25x to 4.0x range, format conversion, and MP3 and WAV auto-combine for long text. The slim variant is described as roughly 100MB and minimal: basic TTS, MP3 and WAV only, WAV auto-combine for long text, and explicitly no speed adjustment, no format conversion, and no MP3 auto-combine.

Read that list again, because the asymmetry is odd. Long text works in both images, but when the requested format is MP3 the slim image cannot stitch the chunks back together. So a long MP3 request against the slim image either fails or returns something other than one continuous file, depending on how the error path is implemented. The README does not say which. If you are deploying this, the slim image is only safe when your inputs are short and your output format is WAV or a short MP3. The README recommends the full variant, and the feature list gives a concrete reason to follow that recommendation rather than a vague one.

Getting it running, and the endpoint that tells you what you got

Installation is two commands. For the Python package, pip install ttsfm for the core client, or pip install ttsfm[web] to pull in the web and server dependencies. For Docker, docker run -p 8000:8000 dbcccc/ttsfm:latest for the full image, or dbcccc/ttsfm:slim for the minimal one. The container serves the web playground at http://localhost:8000 and the OpenAI-compatible route at /v1/audio/speech.

Before you build anything on top, hit the capabilities endpoint: curl http://localhost:8000/api/capabilities. The v3.5.0 release notes describe this as a runtime capabilities API for checking feature availability, and it exists precisely because the two images differ. If your code assumes speed adjustment and you deployed slim, this call is how you find out at startup rather than in production.

From the Python side, the client is constructed with no arguments in the README example, and generate_speech takes text, voice, response_format and an optional speed. Voices and formats are exposed as enums (Voice.ALLOY, AudioFormat.MP3), and the response object has a save_to_file method that appends the extension. There is also a CLI: ttsfm "Hello, world" --voice nova --format mp3 --output hello.mp3. The REST example sends model as tts-1, which is a compatibility field rather than a real model selector; the README does not describe it changing behaviour.

What breaks, and when this is the wrong tool

The largest limitation is not technical. A reverse-engineered client depends on an upstream service that has made no commitment to it. The README does not document rate limits, quota behaviour, or what happens when openai.fm changes its request format or blocks a caller. There is no stated retry policy, no circuit breaker, and no fallback path described. When the upstream changes, the failure mode is a broken endpoint with whatever error the proxy surfaces, and the fix is a new release of TTSFM. The gap between v3.4.2 in December 2025 and v3.5.0 in May 2026 shows that releases are not tied to upstream breakage, so you cannot infer responsiveness from version cadence.

There are smaller constraints too. Speed adjustment costs an ffmpeg pass over the audio, which adds latency proportional to clip length; the README does not quantify it. The voice list is fixed at 11 names, and the README offers no way to add or clone a voice. The model field in the request is accepted for compatibility but is not documented as selecting anything. And the licence position is split: the repository is MIT, but the disclaimer restricts use to educational and research purposes and puts responsibility for compliance with applicable laws and terms of service on the user. The MIT grant covers the code in this repository, not the upstream service the code calls. If you need a speech vendor with a contract, an SLA, or a data-processing agreement, TTSFM is the wrong tool and no amount of Docker tuning changes that.

The realistic alternative, and where the approaches diverge

The obvious alternative is OpenAI's own TTS endpoint, which is what TTSFM imitates. The difference is not the API shape, since TTSFM copies it deliberately. The difference is where the audio is produced and who is accountable for it. With OpenAI you send the same JSON to api.openai.com with an API key, you pay per character, and you get a documented service with published limits and a support path. With TTSFM you send the same JSON to localhost, you pay nothing, and the audio is produced by openai.fm through a reverse-engineered path with no stated guarantees. One is a vendor relationship; the other is a local proxy over someone else's free endpoint.

A second alternative is a self-hosted open-weight TTS model, which flips the trade-off again. That route gives you full control over the model, the voices and the data path, at the cost of running inference hardware and giving up the OpenAI request shape unless you write the adapter yourself. TTSFM sits between the two: OpenAI-compatible surface, no local inference, no contract. Which of the three is right depends entirely on whether the constraint you are solving is cost, control, or compliance. TTSFM only wins on cost and convenience.

Maintenance cost and what to verify before you depend on it

The maintenance burden here is mostly upgrade risk rather than code volume. The project is Python, MIT-licensed, and small enough that the interesting surface is the Docker image and the upstream dependency. The upgrade path that matters is the image tag: latest and slim are the two documented entry points, so pinning to a digest is the only way to make a deployment reproducible, and the README does not discuss pinning. The capabilities endpoint is the other upgrade tool, since it lets your integration fail loudly at startup if a feature disappeared between images.

The things to verify before adopting, in order: run the full image, not slim, unless you have confirmed your inputs are short and your format is WAV; call /api/capabilities and assert on the specific features your code uses, particularly speed and the four ffmpeg-only formats; confirm that openai.fm permits your usage pattern, because the MIT licence on this repository does not grant you anything with respect to that service; and decide in advance what your endpoint does when the upstream returns an error, since the README documents no retry or fallback behaviour. If you cannot answer the fourth question, you are not ready to put this in front of users.

Editorial conclusion

Adopt TTSFM if you want a local endpoint that speaks the OpenAI /v1/audio/speech request shape and you accept that the audio ultimately comes from someone else's reverse-engineered service. Do not adopt it for a paid product, for anything requiring an uptime or content guarantee, or where you cannot accept the README's own educational-and-research restriction. Before committing, run the full image rather than slim, call /api/capabilities and compare it against the feature list you need, and confirm with the operator of openai.fm that your usage is permitted, because the MIT licence on this repository does not extend to the upstream service it calls.

Official sources

  1. dbccccccc/ttsfm on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Community notes

Community notes