Speech emotion detection with a decision model: spectrogram plots + voice features vs your own baseline, decided by Cloudflare Clef. Realtime dashboard with VAD, Docker-only.
Python
3
1 commits
updated Oct 9, 2026
Speech emotion detection with a decision model. Turn a voice into labelled plots and plain-language measurements, then let Cloudflare Clef decide how the speaker feels.
Dashboard playing benchmark clips through a virtual microphone; results are clef-flash's real answers.
SpectroMood is an open experiment: can a general-purpose decision model, which was never trained on audio, recognise emotion in speech if we show it the right evidence? Clef reads a short state, up to four images and a typed question, and returns a probability for every allowed answer. We give it a picture of the voice, the speaker's voice measured against their own calm baseline, and a short guide to how emotions sound, then ask: angry, fearful, happy, sad or neutral?
clef-flash with the recommended plots+features+guide setup, 50 clips per language
(10 per emotion: angry, fearful, happy, sad, neutral). Chance is 20%.
| language | dataset | speaker baseline | accuracy | macro-F1 | arousal AUC | 😠 angry | 😨 fearful | 😄 happy | 😢 sad | 😐 neutral |
|---|---|---|---|---|---|---|---|---|---|---|
| English | RAVDESS | per speaker | 48% | 0.39 | 0.93 | 90% | 0% | 50% | 0% | 100% |
| Russian | RESD | corpus average | 40% | 0.31 | 0.83 | 50% | 0% | 100% | 0% | 50% |
| Spanish | MESD | corpus average | 26% | 0.16 | 0.66 | 0% | 0% | 50% | 0% | 80% |
What each setup sends to Clef (accuracy):
| setup | image | text | English | Russian | Spanish |
|---|---|---|---|---|---|
spectrogram | log-mel spectrogram | – | 20% | 20% | 20% |
plots | spectrogram + pitch + loudness plots | – | 20% | 20% | 20% |
features | – | voice measurements vs baseline | 40% | 38% | 20% |
plots+features+guide | plots | measurements + cue guide | 48% | 40% | 26% |
Arousal AUC measures how well the arousal score separates high-energy speech (angry, fearful, happy) from low-energy speech (sad, neutral): 0.5 is chance, 1.0 perfect. With 50 clips per language the 95% interval on accuracy is roughly ±14 points. Full tables: reports/results.md.

You need Docker and a Cloudflare account. Create an API token with Workers AI permission (dashboard → My Profile → API Tokens → "Workers AI" template).
git clone https://github.com/ketul93/spectromood.git && cd spectromood
cp .env.example .env # set CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN
docker compose up -d --build
Open http://localhost:8000:
Browsers only allow the microphone on localhost or over HTTPS. To use it from another device, put
the container behind HTTPS (for example a Cloudflare Tunnel); file analysis works over plain HTTP.
Everything runs in the tools container; nothing needs to be installed locally.
docker compose run --rm tools python scripts/prepare_data.py --lang en,it,ru,es
docker compose run --rm tools python scripts/evaluate.py --lang en,it,ru,es
docker compose run --rm tools pytest
The clip lists in data/<lang>/ are committed, so every run evaluates the same recordings. Every
Clef answer is cached under a hash of the exact request, so a re-run only pays for new requests.
Results land in reports/results.md and the figures in docs/images/.
Italian (--lang it) is prepared the same way. Evaluating all four languages makes 800 Clef calls
(four setups × 50 clips each), well within one day of the Workers Free allocation.
clef model scored lower than the 9B clef-flash (it labels
almost any raised voice "angry"), and clef-flash is faster and cheaper, so it is the only model used.prepare_data.py --lang it, but it has not been evaluated yet. Run
evaluate.py --lang it (200 Clef calls) and send a PR with the numbers.data/<lang>/manifest.csv and
baselines.csv (columns file,emotion,speaker and file,speaker), put the WAVs in
data/<lang>/clips/, and register the language in scripts/evaluate.py.clef-flash costs $0.09 per million input tokens at launch prices, roughly $0.0002 per request,
or about $0.20 per hour of continuous microphone use (one request every 3 s).src/spectromood/ audio prep, features, plots, Clef client, prompts, FastAPI dashboard
scripts/ prepare_data.py (download clips), evaluate.py (benchmark + figures)
data/<lang>/ committed clip lists: manifest.csv, baselines.csv
worker/ optional Cloudflare Worker that proxies Clef via the Workers AI binding
reports/ benchmark results
The code is Apache-2.0. The audio is not redistributed; prepare_data.py downloads it from the
original sources, which keep their own licences:
| language | dataset | licence | speakers |
|---|---|---|---|
| English | RAVDESS (Livingstone & Russo, 2018) | CC BY-NC-SA 4.0 | 24 actors |
| Italian | Emozionalmente via CAMEO | CC BY 4.0 | many, non-professional |
| Russian | RESD via CAMEO | MIT | not labelled (corpus baseline) |
| Spanish | MESD via CAMEO | CC BY 4.0 | not labelled (corpus baseline) |
The example images in docs/images/ are derived from these recordings under the same terms.
Cloudflare for Clef and Workers AI; the authors of RAVDESS, Emozionalmente, RESD, MESD and Rasa; the CAMEO collection for packaging many emotional speech corpora in one place.
Speech emotion detection with a decision model: spectrogram plots + voice features vs your own baseline, decided by Cloudflare Clef. Realtime dashboard with VAD, Docker-only.
Python
3
1 commits
updated Oct 9, 2026
Speech emotion detection with a decision model. Turn a voice into labelled plots and plain-language measurements, then let Cloudflare Clef decide how the speaker feels.
Dashboard playing benchmark clips through a virtual microphone; results are clef-flash's real answers.
SpectroMood is an open experiment: can a general-purpose decision model, which was never trained on audio, recognise emotion in speech if we show it the right evidence? Clef reads a short state, up to four images and a typed question, and returns a probability for every allowed answer. We give it a picture of the voice, the speaker's voice measured against their own calm baseline, and a short guide to how emotions sound, then ask: angry, fearful, happy, sad or neutral?
clef-flash with the recommended plots+features+guide setup, 50 clips per language
(10 per emotion: angry, fearful, happy, sad, neutral). Chance is 20%.
| language | dataset | speaker baseline | accuracy | macro-F1 | arousal AUC | 😠 angry | 😨 fearful | 😄 happy | 😢 sad | 😐 neutral |
|---|---|---|---|---|---|---|---|---|---|---|
| English | RAVDESS | per speaker | 48% | 0.39 | 0.93 | 90% | 0% | 50% | 0% | 100% |
| Russian | RESD | corpus average | 40% | 0.31 | 0.83 | 50% | 0% | 100% | 0% | 50% |
| Spanish | MESD | corpus average | 26% | 0.16 | 0.66 | 0% | 0% | 50% | 0% | 80% |
What each setup sends to Clef (accuracy):
| setup | image | text | English | Russian | Spanish |
|---|---|---|---|---|---|
spectrogram | log-mel spectrogram | – | 20% | 20% | 20% |
plots | spectrogram + pitch + loudness plots | – | 20% | 20% | 20% |
features | – | voice measurements vs baseline | 40% | 38% | 20% |
plots+features+guide | plots | measurements + cue guide | 48% | 40% | 26% |
Arousal AUC measures how well the arousal score separates high-energy speech (angry, fearful, happy) from low-energy speech (sad, neutral): 0.5 is chance, 1.0 perfect. With 50 clips per language the 95% interval on accuracy is roughly ±14 points. Full tables: reports/results.md.

You need Docker and a Cloudflare account. Create an API token with Workers AI permission (dashboard → My Profile → API Tokens → "Workers AI" template).
git clone https://github.com/ketul93/spectromood.git && cd spectromood
cp .env.example .env # set CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN
docker compose up -d --build
Open http://localhost:8000:
Browsers only allow the microphone on localhost or over HTTPS. To use it from another device, put
the container behind HTTPS (for example a Cloudflare Tunnel); file analysis works over plain HTTP.
Everything runs in the tools container; nothing needs to be installed locally.
docker compose run --rm tools python scripts/prepare_data.py --lang en,it,ru,es
docker compose run --rm tools python scripts/evaluate.py --lang en,it,ru,es
docker compose run --rm tools pytest
The clip lists in data/<lang>/ are committed, so every run evaluates the same recordings. Every
Clef answer is cached under a hash of the exact request, so a re-run only pays for new requests.
Results land in reports/results.md and the figures in docs/images/.
Italian (--lang it) is prepared the same way. Evaluating all four languages makes 800 Clef calls
(four setups × 50 clips each), well within one day of the Workers Free allocation.
clef model scored lower than the 9B clef-flash (it labels
almost any raised voice "angry"), and clef-flash is faster and cheaper, so it is the only model used.prepare_data.py --lang it, but it has not been evaluated yet. Run
evaluate.py --lang it (200 Clef calls) and send a PR with the numbers.data/<lang>/manifest.csv and
baselines.csv (columns file,emotion,speaker and file,speaker), put the WAVs in
data/<lang>/clips/, and register the language in scripts/evaluate.py.clef-flash costs $0.09 per million input tokens at launch prices, roughly $0.0002 per request,
or about $0.20 per hour of continuous microphone use (one request every 3 s).src/spectromood/ audio prep, features, plots, Clef client, prompts, FastAPI dashboard
scripts/ prepare_data.py (download clips), evaluate.py (benchmark + figures)
data/<lang>/ committed clip lists: manifest.csv, baselines.csv
worker/ optional Cloudflare Worker that proxies Clef via the Workers AI binding
reports/ benchmark results
The code is Apache-2.0. The audio is not redistributed; prepare_data.py downloads it from the
original sources, which keep their own licences:
| language | dataset | licence | speakers |
|---|---|---|---|
| English | RAVDESS (Livingstone & Russo, 2018) | CC BY-NC-SA 4.0 | 24 actors |
| Italian | Emozionalmente via CAMEO | CC BY 4.0 | many, non-professional |
| Russian | RESD via CAMEO | MIT | not labelled (corpus baseline) |
| Spanish | MESD via CAMEO | CC BY 4.0 | not labelled (corpus baseline) |
The example images in docs/images/ are derived from these recordings under the same terms.
Cloudflare for Clef and Workers AI; the authors of RAVDESS, Emozionalmente, RESD, MESD and Rasa; the CAMEO collection for packaging many emotional speech corpora in one place.