Express2Text

Turns a short English recording into plain text, trained from scratch on read speech

Released Serving since 2026-09-20 on the Express Voice GPU service, behind /v1/transcribe Default Transcription v1.0.0

Version 1.0.0 · Updated 2026-09-20 · Express · Model 03 / 03

← Overview page

On this page

Overview #

Express2Text listens to a short recording of someone speaking English and writes down the words. You send the clip — a voice note, a dictated sentence, a spoken command — and get back plain lower-case text, with no punctuation and numbers as the words that were said. Use it when an app needs the words in a recording and should not send that recording to a third-party service.

Express2Text is a 20M-parameter conformer acoustic model with a connectionist temporal classification (CTC) head, trained from scratch: no pre-trained weights, no external speech model, only open data. It reads the recording as 80-bin log-mel frames, subsamples them four times, passes them through twelve conformer blocks and emits, for every 40 ms of audio, a distribution over 29 symbols — the letters a to z, the space, the apostrophe and the CTC blank. Greedy decoding collapses that sequence into the transcript.

It is the Default and only member of the Express series' Transcription family. Its siblings go the other way: Express Voice turns text into speech and Express Cue turns a description into a sound cue. All three share the audio quota bucket, and Express2Text runs on the same warm GPU container as Express Voice.

Intended use #

  • Transcribing short voice notes, dictated lines and spoken commands in English, recorded close to the microphone in a quiet room.
  • Turning a spoken reply into text that a responder or a form can use, where the words matter more than the punctuation.
  • Keeping audio inside an organisation's own hosting, on the shared service or a dedicated one, instead of a third-party transcription API.

Out of scope #

  • Languages other than English, and accents or vocabulary far from read audiobook speech.
  • Long recordings, meetings, several speakers at once, music, and audio with heavy noise or reverberation.
  • Punctuation, casing, numerals as digits, timestamps, speaker labels or confidence scores: the output is bare lower-case text.
  • Any use where a mis-heard word could hurt someone — medical, legal, emergency or safety-critical dictation.

Choose Express2Text when #

  • The clip is short, English and reasonably clean, and plain lower-case text is what you need.
  • You need speech turned into text without leaving the Falcon API and its hosting.
  • You need the reverse — text spoken aloud — choose Express Voice on /v1/speak.

Architecture #

Parameters20,235,597
KindConformer encoder with a connectionist temporal classification (CTC) head
Input features80-bin log-mel filterbank, 25 ms window, 10 ms hop, per-utterance mean and variance normalisation
SubsamplingTwo 3×3 stride-2 convolutions (4×, 40 ms per frame) then a linear layer to 256
Blocks12 conformer blocks: half-step feed-forward (×4), 4-head self-attention with a learned relative-position bias (±512 frames), depthwise convolution (kernel 31, batch norm, SiLU), half-step feed-forward, LayerNorm
Vocabulary29 symbols: CTC blank, space, apostrophe and a–z
DecodingGreedy CTC (best symbol per frame, repeats collapsed, blanks dropped); no language model, no beam search
Training4 passes over 960 h (47,920 steps of ≤ 30,000 frames), AdamW, lr 2e-3 with warm-up and cosine decay, SpecAugment, 3-way speed perturbation
Training platformVertex AI, 1× NVIDIA A100 40 GB, bf16 autocast

The front-end is fixed and computed at request time: the recording is decoded, mixed down to one channel, resampled to 16 kHz with a windowed-sinc filter, and turned into 80-bin log-mel filterbank frames (25 ms window, 10 ms hop, 20 Hz to 7.6 kHz) that are normalised to zero mean and unit variance per utterance. Two 3×3 convolutions with stride 2 reduce the frame rate four times, so the encoder sees one vector per 40 ms, projected to 256 dimensions.

Twelve conformer blocks follow. Each applies, with residual connections, a half-step feed-forward layer (256 → 1,024 → 256, SiLU), multi-head self-attention with four heads and a learned relative-position bias clipped at ±512 frames, a convolution module (pointwise expansion with a gated linear unit, a depthwise convolution of kernel 31 with batch normalisation and SiLU, and a pointwise projection), a second half-step feed-forward layer, and a final LayerNorm. Padding frames are masked out of the attention and of the convolutions, so a clip transcribes the same whether it is sent alone or padded in a batch.

A linear layer maps each frame to 29 log-probabilities. There is one head and one fixed symbol set; the CTC blank is what lets the model emit nothing for frames between letters. Decoding is greedy — the most likely symbol per frame, repeats collapsed, blanks removed — with no language model and no beam search, which keeps inference to one forward pass.

What is deliberately absent: no pre-trained encoder, no language model, no punctuation or casing model, no speaker embedding, no voice-activity detection and no streaming mode; the whole clip is read at once.

Comparison #

ModelFamilyParametersDirectionRouteBase
Express2TextTranscription20,235,597speech → text/v1/transcribeNone, from scratch
Express VoiceVoiceabout 144Mtext → speech/v1/speakSpeechT5 fine-tune
Express CueSound24,447,786description → sound/v1/soundNone, in-house engine

Express2Text and Express Voice share a container and a quota bucket but nothing else: different inputs, different outputs and different training data.

Inputs & outputs #

Input #

FieldTypeRequiredDescriptionLimit
audiostring (base64 or data URL)YesThe recording as base64, plain or as a data:…;base64, URL. Aliases: audio_base64, wav, wav_base64. WAV, FLAC or OGG at any sample rate and channel count; resampled to 16 kHz mono.12,000,000 decoded bytes; the first 60 s are transcribed

A complete request:

json
{
  "audio": "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAgD4AAAB9AAACABAAZGF0YQ…"
}

The clip may be WAV (any bit depth libsndfile reads), FLAC or OGG Vorbis; MP3, M4A and CAF are not accepted, so a host that records in those formats converts first. Sample rate and channel count are free: the service mixes to mono and resamples to 16 kHz. At most 12,000,000 decoded bytes are accepted (about 60 s of 48 kHz stereo 16-bit WAV, or several minutes of FLAC); whatever the size, only the first 60 seconds are transcribed and a longer clip is reported as truncated.

Output #

FieldTypeDescription
okbooleantrue on success; false with an error code otherwise.
enginestringThe fixed string express2text.
textstringThe transcript: lower-case letters, spaces and apostrophes only; no punctuation, and numbers as the words that were spoken. Empty when nothing intelligible was heard.
duration_secondsnumberLength of the clip as decoded.
truncatedbooleanPresent and true when the clip was longer than 60 s and only the first 60 s were transcribed.
json
{
  "ok": true,
  "engine": "express2text",
  "text": "your table is ready please make your way to the front desk",
  "duration_seconds": 3.52
}

Decision-rule semantics: the transcript is the greedy decode of one forward pass, so it is deterministic for a given clip. There is no confidence score; a clip the model cannot make sense of tends to come back short, fragmentary or empty rather than flagged. Treat text as a best reading of the audio, keep the clip when the words matter, and let the host or the reader confirm anything consequential.

Examples #

Reference transcripts and Express2Text's readings from the LibriSpeech test sets, decoded greedily:

SetReferenceExpress2Text
test-cleanverse twofirst two
test-cleana storya story
test-otheri will tell heri will tell her
test-othernonsensenonsense
test-cleanoh emilo amial
test-cleanindeed ahindeeda
test-cleanfarewell madamfare well madame
test-othericesices
test-othera pause ensueda pas an suit
test-otherhave you been in europehoat you been iany your

Training #

Express2Text was trained from scratch — every weight initialised at random — on LibriSpeech, the 960-hour corpus of read English audiobook speech from the LibriVox project (Panayotov, Chen, Povey and Khudanpur, 2015; CC BY 4.0): the three training subsets train-clean-100, train-clean-360 and train-other-500, 281,241 utterances from about 2,300 speakers, with dev-clean and dev-other held out for model selection and test-clean and test-other for the reported evaluation. No real user data, no recordings from the host application and no other corpus were used.

Training ran as one Vertex AI custom job on a single NVIDIA A100 40 GB on 2026-09-20: 4 passes over the training set — 47,920 steps, about 195 minutes of which 86 were the data download — with bf16 autocast. Each step holds up to 30,000 padded 10 ms frames (about five minutes of audio) in duration-bucketed batches; every utterance is decoded from FLAC, resampled to 16 kHz with a 3-way speed perturbation (0.9×, 1.0×, 1.1×), turned into 80-bin log-mel frames normalised per utterance, and masked with SpecAugment (two frequency masks of up to 27 bins, two time masks of up to 5 % of the frames). The optimiser is AdamW (betas 0.9 and 0.98, weight decay 0.01) at a peak learning rate of 2e-3 with a 3,000-step linear warm-up and cosine decay to a twentieth of the peak, gradient-norm clipping at 5, and the CTC loss over the 29-symbol vocabulary. Every 2,000 steps the model was decoded greedily on 800 utterances each of dev-clean and dev-other; the checkpoint with the lowest dev-clean word error (step 46,000: 11.8 % dev-clean, 28.6 % dev-other) is the served one. Utterances longer than 35 seconds were skipped in training (they are rare in LibriSpeech). No language model is trained or used.

A pilot on train-clean-100 alone (100 h) established the recipe first: with the same settings, word error on dev-clean fell from 78.5 % after 1,500 steps to 28.1 % after 10,500 steps, at about 0.13 s per step. The full run started from fresh weights, not from the pilot.

Evaluation #

MetricValueSource
Word error rate, LibriSpeech test-clean (2,620 utterances, 5.4 h)12.1 %eval.json of the 2026-09-20 evaluation (greedy CTC decoding, no language model); the 100-hour pilot scored 15.1 %
Word error rate, LibriSpeech test-other (2,939 utterances, 5.3 h; noisier, accented)25.5 %eval.json of the 2026-09-20 evaluation; the pilot scored 35.4 %
Character error rate, test-clean / test-other3.6 % / 9.6 %eval.json of the 2026-09-20 evaluation
Empty transcripts on the test sets0 of 5,559eval.json of the 2026-09-20 evaluation
Validation word error rate during training, dev-clean / dev-other11.8 % / 28.6 % at step 46,000 of 47,920meta.json checkpoint sidecar

The reported word error rates are on the two LibriSpeech test sets, never seen in training or model selection, transcribed with the served decoding (greedy, no language model) by express2text/evaluate.py: 12.1 % on test-clean (2,620 utterances from clean read speech) and 25.5 % on test-other (2,939 utterances with more accented and noisier recordings), with character error rates of 3.6 % and 9.6 %. No transcript came back empty. Typical errors are homophones and rare words spelled as they sound (“verse two” heard as “first two”): the model has no language model to prefer likely words, only the acoustics.

For scale, the 100-hour pilot with the same recipe reached 15.1 % and 35.4 %; a well-tuned conformer of this size trained for many more passes with a language model reaches about 3 % on test-clean in the literature, so Express2Text 1.0.0 sits where a four-pass, decoder-free model of 20M parameters should. The A100 needed 3.2 hours; on an Apple M5 Pro the evaluation transcribed a clip in 7 ms on average.

Known gaps: the reported figures use greedy decoding without a language model, which is how the service runs, and cover read audiobook English only. Nothing here measures voice notes recorded on a phone, spontaneous or accented speech, noisy rooms, children's voices or non-English audio; on such input the error rate will be higher, sometimes much higher. There is no human rating and no measurement of proper-noun accuracy.

API #

Express2Text is exposed through the Falcon API route /v1/transcribe. Authentication, the envelope, retry guidance and the full error vocabulary are documented under API conventions; this section covers what is specific to Express2Text.

EndpointAuthBody limitDescription
POST /v1/transcribeBearer preview key (fln_) or session token (fls_)17.5 MBTranscribe one recording; counts one unit of the audio bucket.
GET /healthnonenoneFalcon API health; the expressText field reports whether a transcription endpoint is configured.

Route /v1/transcribe · quota bucket audio · body limit 17.5 MB JSON body (12 MB of decoded audio as base64).

http
POST /v1/transcribe HTTP/1.1
Authorization: Bearer fln_your_preview_key
Content-Type: application/json
json
{ "audio": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAgD4AAAB9AAACABAAZGF0YQ…" }
json
{ "ok": true, "engine": "express2text", "text": "your table is ready please make your way to the front desk", "duration_seconds": 3.52 }

Errors #

StatusCodeMeaning
400bad_jsonBody is not valid JSON or exceeds the 17.5 MB limit.
400audio_requiredNo clip in audio or its aliases.
400bad_audioThe clip is not decodable WAV, FLAC or OGG.
400audio_too_largeDecoded clip exceeds 12,000,000 bytes; maxBytes is returned.
401invalid_credentialsMissing, malformed or revoked preview key or session token.
429quota_exceededThe audio preview quota bucket for this UTC calendar month is used up.
502transcribe_failedExpress2Text failed after a valid request.
503transcribe_warmingThe voice service hosting Express2Text is loading a replacement instance; retry after the Retry-After interval (30 s).
503transcribe_unavailableNo voice service is configured, or this organisation's dedicated voice service predates Express2Text; not counted.

Every successful call counts one unit against the audio preview quota bucket, tallied per UTC calendar month.

Runtime & deployment #

KindGPU service
ResidentLoaded beside the Express Voice models on their always-warm NVIDIA L4 container; weights load once at start-up, independently of the voices
ServingThe Express Voice GPU service's /v1/transcribe, behind the Falcon API route POST /v1/transcribe
Cold startNone in normal operation: the service keeps one warm instance; while a replacement instance loads, the route answers 503 transcribe_warming with Retry-After 30
ConcurrencyShared with Express Voice on the one warm instance
Timeout45 s per request at the caller, with one automatic retry after 2.5 s on 503 or timeout

Express2Text runs on the Express Voice Cloud Run GPU service — one NVIDIA L4, kept warm with one minimum instance — loaded as a third model beside the two voice models, from its own checkpoint and independently of them: if the voices fail to load, transcription still answers, and the other way round. The service reports its state under transcribe in its health payload. Because the instance is warm there is no cold start in normal operation; the only wait is while Cloud Run replaces an instance, when the Falcon API answers 503 transcribe_warming with Retry-After: 30. Measured against the service on 2026-09-20 with ten test-clean clips of 2–18 seconds: a median of 0.24 s and at most 0.32 s per request, network round trip included, so the audio decode and the single forward pass are a small part of the wait.

What the host must provide:

  • A WAV, FLAC or OGG clip of at most 60 s and 12 MB, as base64; conversion from other formats happens on the host.
  • Retry handling for 503 transcribe_warming beyond the single built-in retry.
  • A presentation layer that treats text as a reading of the audio, not as a verified record.

Integration notes #

  • Record close to the microphone, in a quiet room, at 16 kHz or higher; the model was trained on clean read speech and degrades quickly with noise and reverberation.
  • Keep clips short and single-speaker; split a long recording into sentences on the host before sending.
  • Expect lower-case text without punctuation: add casing and punctuation on the host if a display needs them, and expect numbers as words (“twenty three”, not “23”).
  • Keep the clip when the words matter; the transcript carries no confidence score.
  • On the media path of /v1/spatial there is no transcription step; Express2Text is a separate call.

Limits & safety #

It hears only what the microphone captured, and it was trained only on clean, read English audiobooks.

  • English only, and best on read speech: spontaneous, accented, fast or overlapping speech is transcribed less reliably, sometimes badly.
  • No punctuation, casing, digits or speaker labels: the output is a bare stream of lower-case words.
  • No confidence score: a misheard word looks exactly like a correct one, and a clip the model cannot follow can come back empty rather than flagged.
  • Sixty-second clips at most: longer audio is cut, and a clip with long silences or music in it produces fragments.
  • Not a substitute for a human transcript in any medical, legal, emergency or safety-critical setting, and not a speaker-identification or voice-verification system.

Out of scope: languages other than English, meeting transcription, streaming, diarisation and anything that needs timestamps.

Fixed weights per version; the model does not learn from requests.

Versions #

VersionDateStatusNote
1.0.0ReleasedFirst version: trained from scratch on LibriSpeech in one 3.2-hour A100 job (4 passes); 12.1 % / 25.5 % word error on test-clean / test-other with greedy decoding.

Compatibility: a major version bump changes the input or output contract — the accepted formats, the 60-second limit, the symbol set or the shape of the /v1/transcribe envelope; a minor bump is a retrain on the same contract; a patch bump touches only metadata or the runtime. Within version 1.x a request written today stays valid.

Current weights: model.pt, the step-46,000 checkpoint of the 2026-09-20 job (dev-clean word error 11.8 %), 81,083,754 bytes, float32 PyTorch checkpoint with a meta.json sidecar. Weights are not distributed during the private preview.

References #

Trained from scratch, so no base-model licence applies; the training corpus LibriSpeech (Panayotov, Chen, Povey and Khudanpur, 2015) is CC BY 4.0, attributed here and in the Express series notes; weights are not distributed during the private preview.