Metric
Rank by

Note: Many models do not disclose their training data. Some models may have been trained on one or more of the evaluation datasets, in which case their scores are not directly comparable to models that have not.

Size tradeoffs

Pareto frontier

Compare score and size. Models appear by default. In All, the blue frontier spans Models and Systems; leaderboard Pareto badges compare each type separately. System size may omit extra components or hosted compute.

Metric
Rank by

Performance vs. Size

Entries above 30% mean WER are omitted from this plot and its CSV; change the limit in Settings.

Leaderboard history

Performance over time

Track model scores and the best result available at each plotted date.

Metric
Rank by

Performance Over Time

Models above 30% mean WER are excluded; change the limit in Settings.

Open weights Proprietary API Best at date

Side by side

Compare models

Compare up to four models on the same test sets, including speaker groups and divergent transcripts. Replay audio where available.

0 / 4 models
Add a model to see its results.

The benchmark

About

An open, reproducible benchmark for Danish speech-to-text. Every model is transcribed and scored the same way on the same five public Danish test sets, so the numbers compare directly: Word Error Rate (WER) and Character Error Rate (CER) — lower is better — plus speed. Open-weight models you can run yourself and hosted transcription APIs are both included.

Åben benchmark for dansk tale til tekst. Sammenlign hvor præcist forskellige modeller til dansk talegenkendelse omsætter tale til tekst. Alle modeller måles ens på de samme fem offentlige danske testsæt, så tallene kan sammenlignes direkte: ordfejlrate (WER) og tegnfejlrate (CER) — lavere er bedre — samt hastighed. Både åbne modeller, du selv kan køre lokalt, og kommercielle tale-til-tekst-API'er indgår.

As of 23 October 2026, the most accurate Danish speech-to-text model measured here is syv-transcribe at 7.12% WER, ahead of danish-foundation-models/edda-v0.2-duo (7.73%) and Harmonium/brage-v1 (8.13%). The most accurate open-weight model is danish-foundation-models/edda-v0.2-duo at 7.73% WER. 48 models are compared across 5 public Danish test sets.

Test sets

ColumnDatasetSplitDomain
CoRal Conv. CoRal-project/coral-v3 — conversation testSpontaneous conversation
CoRal Read CoRal-project/coral-v3 — read_aloud testRead-aloud speech
CV (da) Common Voice 27.0 — da, 2,756 clips (the CV 25.0 test split) testCrowd-sourced read speech
FLEURS (da) google/fleurs — da_dk testRead speech
FTSpeech alexandrainst/ftspeech test_balancedParliamentary / broadcast

How the leaderboard is scored

Methodology

The same scoring pipeline is applied to every model. These are the text rules, metrics, and evaluation conditions behind the results.

Text normalisation

Applied identically to hypothesis and reference before scoring:

  1. Unicode NFKC — folds compatibility characters (ligatures, full-width digits, ²→2) so visually equivalent forms match; optional acute accents such as dér/der are ignored
  2. Exact word equivalents — selected historical and joined spellings (saa/så, afsted/af sted), abbreviations (fx/for eksempel, bl.a./blandt andet, m.m./med mere), units (km, cm, kg, kr, pct, numeric m, mm, m², km/h, km/t, m/s), and numeric mio./mia. match their spoken forms
  3. Danish number canonicalisation — thousands separators are removed (1.234 → 1234), while decimals retain a marker (3.14 / 3,14 → 3komma14). Trailing decimal zeroes, frequency-unit formatting (including 2.400 GHz = 2.4 GHz), % / procent, clock times and ratios are normalized. Numeric slashes split numbers without assuming a fraction or date
  4. Lowercase
  5. Strip punctuation — apostrophes inside a word (e.g. det's) are preserved; all other punctuation and symbols are removed
  6. Spelled-out numerals → digits — numbers written as words are folded to their digit form with text2num (otte og tredive and otteogtredive → 38; hundrede → 100), so spelling and spacing variants collapse onto one value before the expansion below. Bare en/et are left as indefinite articles
  7. Numerals → Danish words — standalone integers are spelled out with num2words (4→fire, 24→fireogtyve, 100→ethundrede), so a model that writes "4" is not penalised against a reference that spells out "fire". Only clean standalone integers are converted, so decades such as 1960'erne keep their digits
  8. Collapse whitespace

A dash between two digits becomes a space during the punctuation strip rather than being deleted, so 1-3 scores as two numbers. Deleting it glued the range into 13 → tretten, and the time range 10 00-11 00 into ti elleve nul — references no correct transcription could match, which also handed free matches to models that emit digits.

Known limitation: a bare 2.400 is read as two thousand four hundred; GHz context is needed for the decimal reading. A numeric slash separates its operands, but does not infer whether they form a fraction or date. Ordinals written with a full stop are likewise left alone (3. plads → tre plads): converting them to tredje was measured and rejected, since most N. here are sentence-final cardinals rather than true ordinals.

This is broadly consistent with the HF Open ASR Leaderboard (BasicTextNormalizer) and matches the de-facto Danish standard set by danish-speech-eval, whose "normalised WER" likewise spells numerals out as words.

Note: folding digit↔word formatting is a fairness correction, not a leniency — it is applied symmetrically to reference and hypothesis, so genuine number-recognition errors are still counted.
Note: Danish hesitation fillers (øh, hmm, …) are removed, from reference and hypothesis alike, using the same pattern as alexandrainst/coral. Scoring them verbatim measured transcription convention rather than accuracy. Verbatim scoring remains available offline via --no-filler-words.
Note: General Danish spelling substitutions (aa↔å, oe↔ø, ae↔æ) are not applied — these letter sequences occur legitimately in names such as Aarhus. Only a short, explicit list of historical words is folded.

Models and systems

The entry switch shows all results, single-checkpoint Models decoded greedily or with plain beam search, or Systems that add a language model or reranker, combine checkpoints, or use a hosted API. The small label by each name describes the submitted setup. A model may have one result of each type; the two scores come from separate evaluations. The Pareto chip compares open entries within their own category under the selected metric, ranking method, and filters.

Metric

WER (Word Error Rate, %) and CER (Character Error Rate, %) — lower is better.

WER = (substitutions + deletions + insertions) / reference_words × 100

CER = (char substitutions + deletions + insertions) / reference_chars × 100

Mean WER and Mean CER are macro-averages across all five test sets (equally weighted).

WER can exceed 100 %. The denominator is the reference word count, but the numerator counts insertions without a ceiling — if a model hallucinates or loops, it can emit far more words than the reference contains, pushing WER well above 100 %. This is mathematically correct and a real signal: scores above 100 % on a given dataset indicate the model is not usable for that domain.

Speed

Speed (x) = total audio duration / total inference time. A value of 30x means 30 seconds of audio processed per wall-clock second. Only the transcription call is timed — model load time is excluded. Measured on an NVIDIA A100 (80 GB).

Hosted API models show — instead. For those, the figure would measure network latency and our own request concurrency rather than anything about the provider, so it is not comparable with the locally-run models or between providers.

Speed reflects the serving stack, not just the model. Some models are run through high-throughput engines such as vLLM (continuous batching, paged KV cache), which can be several times faster than a plain transformers generation loop for the same weights. Treat Speed as "best-effort real-world throughput on this GPU" rather than an intrinsic model property — it is not comparable across different serving stacks or hardware, and API models are network-bound.
No warm-up run. Timing starts on the first batch, so one-off costs (CUDA lazy init, kernel autotuning, graph capture) are folded into the measurement. This slightly understates throughput, more so on smaller test sets where the fixed overhead is less amortised.

Code & data

Results dataset: RyeAI/danish-asr-leaderboard
Eval code: github.com/Rye-A1/danish-asr-leaderboard

Adding a model

There are two paths, depending on whether you've run the evaluation yourself:

Request a model — we run it

If you can't run the eval yourself, open a GitHub issue (or a Space discussion) with the model id, backend, and where to find it. We'll run it through the harness and add it.

Submit a score — you ran it

Run the harness on all five test sets, then open a pull request on the GitHub repo committing the two outputs the harness writes, plus a metadata entry:

  1. results/<model-slug>.json — scores and metadata.
  2. outputs/<model-slug>/ — the raw per-dataset transcriptions (<dataset>.jsonl + meta.json), one {"id", "reference", "hypothesis"} per line. Lets reviewers inspect actual transcriptions and enables re-scoring under future normalisers.
  3. An entry in scripts/model_metadata.json: the Model/System type, release date, plus links to the training code, training data, model card and paper, or null where there is none (criteria in CONTRIBUTING.md).

Include in the PR description: the exact command you ran, the hardware used (for speed_x context), and whether the model is open or proprietary. On merge, CI automatically publishes both to the HF dataset and updates the leaderboard.

Do not modify the normalisation or metrics — run the harness as-is so results stay comparable.
Verification: whichever path a model arrives by, we re-evaluate it independently on our own hardware before publishing — to confirm the scores reproduce and catch any configuration differences.
Methodology & data changes

2026-10 — Transcript spelling

  • Optional acute accents, selected historical spellings and common unit abbreviations now match their spoken forms. All 44 saved outputs were re-scored: mean WER fell by 0.07–0.17 percentage points per model, with no rank changes.
  • Additional attested abbreviations, numeric units and joined/spaced words now match. Re-scoring the same 44 outputs changed mean WER by −0.09 to +0.01 percentage points, with no rank changes.

2026-09 — Common Voice column relabelled

  • The Common Voice column was always the Common Voice 25.0 Danish test split (2,756 clips), not 17 as labelled. No scores change; the cv17_da key is kept for compatibility.
  • 25.0 is no longer available, so the harness now builds the same clips from Common Voice 27.0 (byte-identical audio and sentences) and refuses any other set on a full run.

2026-09 — Filler words removed

  • Hesitation fillers are now stripped (øh, hmm, …) from reference and hypothesis alike, using alexandrainst/coral's exact pattern. Verbatim scoring measured convention, not accuracy: models trained to omit disfluencies were penalised for a style choice.
  • Measured over 34 models: mean WER −0.09 pp, no rank changes on mean WER or Avg. Rank. Only coral_conversation reorders (5 models) — the correction being made.
  • Not monotone: whisper-tiny +0.52 and whisper-base +0.41, both already above 96 WER.
  • --no-filler-words restores verbatim scoring offline.

2026-08 — Numeral normalisation

  • Spelled-out numerals now fold onto their digit form. Scoring already expanded digits to Danish words so that 38 and otteogtredive count as the same token. That only worked when the model happened to spell a number the way num2words does: a model writing otte og tredive still mismatched, and because Danish compounds are written closed, three tokens against one cost roughly three word errors rather than one. 100 was expanded to ethundrede, which never matched the spoken hundrede. Numerals are now folded to digits first and expanded once, so spelling and spacing variants land on the same form. The scored text is unchanged in kind — still Danish words — only the set of inputs reaching each form is wider.
  • Effect. Near-uniform: most models improve by 0.10–0.14 WER, which shifts every score together and changes no ranking. The exception is MediaCatch/xls-r-300m-danish-mc-v2, which spells numerals out with spaces throughout and improves by 1.06, moving up two places. Measured across all 29 published models before adoption: 28 improve or hold, and the one regression is whisper-tiny at +0.04 WER, already above 130 %.
  • Not adopted: filler-word stripping. Removing Danish hesitation markers (øh, hmm) was measured at the same time and rejected. It changed no ranking across the 29 models, and it is not monotone — whisper-tiny and whisper-base get worse, because at their error rates removing reference tokens raises the rate. It remains available as an opt-in for offline re-scoring. Superseded in 2026-09 — see below.

2026-08 — Decoding fixes & re-runs

  • CTC models decoded with their n-gram LM. Four entries ship a language model in their own repo (the three CoRal Røst wav2vec2 models and MediaCatch). Beam search was being run over the batch padding as well as the speech, so it invented trailing words — badly on MediaCatch, mildly on Røst. Decoding now trims each clip to its true length first. Røst improves by 0.2–1.0 WER, and MediaCatch/xls-r-300m-danish-mc-v2 joins the board.
  • Speed figures corrected, not improved. Those four models now report 3–9× higher speed (x). The models did not change: beam search previously ran single-threaded and now runs in parallel, so the old figures understated them.
  • Short-form vs long-form decoding. The harness passed return_timestamps=True — a long-form setting — to every clip, on a corpus that is ~99 % short-form. On short audio that sends the Danish Whisper fine-tunes into repetition loops (a one-word reference producing 100+ repeated words), badly inflating their WER. Decoding now follows the reference implementations (HF Open ASR Leaderboard, CoRal): plain short-form under 30 s, timestamped long-form above it. Every transformers-backend model was re-run — roest-v3-whisper-1.5b improves from 31.82 to 21.08 WER on CoRal conversation. The wav2vec2 Røst models are unaffected: they use a CTC path that cannot loop.
  • Whisper hallucination on very short clips. Under standard decoding, whisper-large-v3 hallucinates on ~0.6 % of CoRal conversation utterances — a 5-word reference typically yielding ~220 words — emitting memorised training text such as Danish subtitle credits. This seems to be genuine model behaviour rather than a scoring artifact, and is visible in the published raw outputs. Its earlier score was flattered by the non-standard setting above, which happened to suppress the effect for vanilla Whisper while causing the opposite failure in the fine-tunes.

2026-06 — Full re-evaluation & normalisation update

  • Numerals → words. Standalone integers are now spelled out (num2words, Danish) before scoring, so digit-vs-word formatting ("4" vs "fire") no longer counts as an error. Lowers every model's WER by ~0.1–0.5 pp; does not reorder the board.
  • Unicode NFKC. Switched from NFC to NFKC (compatibility folding). A near-no-op on Danish speech text, adopted for correctness and consistency with the Danish standard.
  • Raw outputs published. Every model's un-normalised per-sample transcriptions are now in the results dataset (outputs/), so anyone can re-score under a different normaliser without re-running inference.
  • Long-audio fix (wav2vec2 family). Upgrading to transformers 5.x fixed a silent truncation bug: the previous transformers 4.x processor had no automatic chunking and silently dropped audio beyond ~30 s, inflating WER on the longer test sets. Affected models were re-measured.
  • scribe_v2 (ElevenLabs). Improved markedly vs the prior run — verified to be a genuine server-side model upgrade (the identical audio re-transcribes ~16 pp better on conversational speech), not a scoring change.
  • Speed re-measured on an NVIDIA A100 (80 GB); earlier figures were on an RTX Pro 5000.
Openness details for
Did we miss something? Open an issue ↗