2026-08-09 22:17:18 +02:00
|
|
|
|
tcdweb - Transcode Detector (Web Version)
|
2026-07-05 00:44:08 +02:00
|
|
|
|
=========================================
|
|
|
|
|
|
|
2026-07-06 18:17:24 +02:00
|
|
|
|

|
|
|
|
|
|
|
2026-07-05 00:44:08 +02:00
|
|
|
|
**tcdweb** is a browser-based audio authenticity analyzer that determines whether
|
|
|
|
|
|
an audio file is a genuine native encode or a *transcode* (lossy → lossless
|
|
|
|
|
|
re-encode). It can also detect *upscaling* (a lossy file re-encoded at a higher
|
|
|
|
|
|
bitrate by the same lossy codec, e.g. 128 → 320 kbps MP3).
|
|
|
|
|
|
|
|
|
|
|
|
This is the **web version** of [tcd](https://codeberg.org/armin/tcd), the
|
|
|
|
|
|
original CLI tool. At this point **tcdweb is a complete, independent fork** with
|
|
|
|
|
|
its own codebase, its own analysis engine, and its own UI. It runs entirely in
|
2026-08-09 22:17:18 +02:00
|
|
|
|
the browser via the **Web Audio API** - no server-side processing, no file
|
2026-07-05 00:44:08 +02:00
|
|
|
|
uploads, no FFmpeg dependency, and no command line needed.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-07-05 22:58:59 +02:00
|
|
|
|
Live-Demo
|
|
|
|
|
|
---------
|
|
|
|
|
|
|
|
|
|
|
|
There is a live demo of tcdweb available at: [bsd.pm/tcdweb](https://bsd.pm/tcdweb).
|
|
|
|
|
|
|
2026-08-09 22:17:18 +02:00
|
|
|
|
Obligatory AI notice
|
|
|
|
|
|
--------------------
|
2026-07-05 00:44:08 +02:00
|
|
|
|
|
2026-08-09 22:17:18 +02:00
|
|
|
|
tcdweb was developed with the assistance of AI.
|
2026-07-05 00:44:08 +02:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
What it does
|
|
|
|
|
|
------------
|
|
|
|
|
|
|
|
|
|
|
|
Drag or load an audio file, and tcdweb will:
|
|
|
|
|
|
|
|
|
|
|
|
1. Decode it client-side using the browser's built-in decoder
|
|
|
|
|
|
2. Compute the frequency spectrum via a Hann-windowed FFT (4096-point, 50% overlap)
|
|
|
|
|
|
3. Extract five key metrics (see below)
|
|
|
|
|
|
4. Apply a two-layer verdict system to classify the file
|
|
|
|
|
|
5. Display a detailed decision log, confidence score, and interactive charts
|
|
|
|
|
|
|
2026-07-06 18:10:58 +02:00
|
|
|
|
It includes a **realtime FFT analyzer** (with 8 window functions, configurable
|
|
|
|
|
|
peak-hold decay, and cutoff max falloff), a **waveform viewer with playback**,
|
2026-07-05 00:44:08 +02:00
|
|
|
|
**BPM detection**, **metadata tag parsing** (ID3v1/v2, FLAC, APE), and a full
|
|
|
|
|
|
data table with every computed value.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
Key metrics
|
|
|
|
|
|
-----------
|
|
|
|
|
|
|
|
|
|
|
|
- **Cutoff**: highest frequency with measurable energy (as Hz and % of Nyquist).
|
2026-08-09 22:17:18 +02:00
|
|
|
|
Lossy codecs chop off high frequencies - the lower the cutoff, the more
|
2026-07-05 00:44:08 +02:00
|
|
|
|
aggressive the compression.
|
|
|
|
|
|
- **Steepness (Transition Bandwidth)**: how abruptly the spectrum drops at the
|
|
|
|
|
|
cutoff point. Lossy encoders produce sharp brick-wall filters (low steepness).
|
|
|
|
|
|
- **Noise Floor**: average noise level in the top quartile of the spectrum.
|
|
|
|
|
|
Lossy quantization raises the noise floor.
|
|
|
|
|
|
- **Roughness (Coefficient of Variation)**: how jagged/bumpy the spectrum is
|
|
|
|
|
|
before the cutoff. Lossy encoding introduces quantization noise that creates
|
|
|
|
|
|
spectral scalloping.
|
|
|
|
|
|
- **Band Ratio**: energy ratio between 16–20 kHz and 12–16 kHz. Transcodes show
|
|
|
|
|
|
an unnatural dip in the top octave.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
Supported formats
|
|
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
|
|
|
|
| Lossy | Lossless |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| MP3 | FLAC |
|
|
|
|
|
|
| AAC / M4A | WAV / AIFF |
|
|
|
|
|
|
| Ogg Vorbis | ALAC |
|
|
|
|
|
|
| Opus | WavPack (WV) |
|
|
|
|
|
|
| WMA | APE |
|
|
|
|
|
|
| AC3 / EAC3 | DSF / DFF |
|
|
|
|
|
|
| MP2 / MP1 | |
|
|
|
|
|
|
|
|
|
|
|
|
Support depends on the browser's built-in decoder (all modern browsers support
|
|
|
|
|
|
the most common formats).
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
Verdicts
|
|
|
|
|
|
--------
|
|
|
|
|
|
|
|
|
|
|
|
| Verdict | Input codec | Meaning |
|
|
|
|
|
|
|---|---|---|
|
2026-08-09 22:17:18 +02:00
|
|
|
|
| **NATIVE** | lossy | Single encode at the stated bitrate - genuine |
|
2026-07-05 00:44:08 +02:00
|
|
|
|
| **UPSCALED** | lossy | Re-encoded from a lower bitrate (e.g. 128 → 320 kbps) |
|
|
|
|
|
|
| **GENUINE** | lossless | No evidence of lossy origin |
|
|
|
|
|
|
| **TRANSCODE** | lossless | Originated from a lossy source, decoded to lossless |
|
|
|
|
|
|
| **SILENT** | any | No detectable audio content |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
Detection logic
|
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
|
|
The verdict is determined in two layers:
|
|
|
|
|
|
|
2026-08-09 22:17:18 +02:00
|
|
|
|
**Layer 1 (Primary)** - Cutoff + Steepness. A low cutoff combined with a sharp
|
2026-07-05 00:44:08 +02:00
|
|
|
|
drop (low steepness) is a definitive sign of a lossy encoder's lowpass filter.
|
|
|
|
|
|
|
2026-08-09 22:17:18 +02:00
|
|
|
|
**Layer 2 (Secondary)** - Roughness + Band Ratio. Applied when the cutoff is
|
2026-07-05 00:44:08 +02:00
|
|
|
|
high enough to pass Layer 1. Catches transcodes where the cutoff is near
|
|
|
|
|
|
Nyquist but the spectrum still shows quantization artifacts.
|
|
|
|
|
|
|
|
|
|
|
|
Lossy files are checked for upscaling by comparing the cutoff ratio to
|
|
|
|
|
|
bitrate-specific expectations.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
Confidence score (0–100%)
|
|
|
|
|
|
-------------------------
|
|
|
|
|
|
|
|
|
|
|
|
A continuous score based on how far the metrics deviate from the thresholds.
|
|
|
|
|
|
Higher confidence means stronger evidence supporting the verdict.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
How it differs from the original tcd CLI
|
|
|
|
|
|
-----------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
| Feature | tcd (CLI) | tcdweb |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| Platform | C program, command line | Pure JavaScript, browser |
|
|
|
|
|
|
| Decoder | FFmpeg (system dep) | Web Audio API (built-in) |
|
|
|
|
|
|
| Analysis | 4096 FFT, same algorithm | 4096 FFT, same algorithm |
|
|
|
|
|
|
| Realtime spectrum | Terminal TUI | Canvas-based interactive chart |
|
|
|
|
|
|
| Playback | No | Yes, with waveform and seek |
|
|
|
|
|
|
| BPM detection | No | Yes (autocorrelation) |
|
|
|
|
|
|
| Metadata parsing | No | ID3v1/v2, FLAC, APE |
|
|
|
|
|
|
| Stereo mode / VBR info | No | Yes (MP3 header parsing) |
|
|
|
|
|
|
| File size limit | None | Browser memory (~2 GB typical) |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
Limitations
|
|
|
|
|
|
-----------
|
|
|
|
|
|
|
|
|
|
|
|
- **Browser audio decoding** limits which formats work (depends on `AudioContext.decodeAudioData`).
|
|
|
|
|
|
- **Very short files** (< 4096 samples) cannot be analyzed.
|
|
|
|
|
|
- **High-bitrate lossy encodes** (320 kbps MP3, 256 kbps AAC) may not be
|
|
|
|
|
|
distinguishable from lossless by cutoff alone.
|
|
|
|
|
|
- **Already-filtered material** (e.g. deliberate 15 kHz LPF during mastering)
|
|
|
|
|
|
may produce false positives.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
References
|
|
|
|
|
|
----------
|
|
|
|
|
|
|
2026-08-09 22:17:18 +02:00
|
|
|
|
- Nyquist–Shannon sampling theorem - Wikipedia
|
2026-07-05 00:44:08 +02:00
|
|
|
|
https://en.wikipedia.org/wiki/Nyquist–Shannon_sampling_theorem
|
2026-08-09 22:17:18 +02:00
|
|
|
|
- Equal-loudness contour (Fletcher–Munson curves) - Wikipedia
|
2026-07-05 00:44:08 +02:00
|
|
|
|
https://en.wikipedia.org/wiki/Equal-loudness_contour
|
2026-08-09 22:17:18 +02:00
|
|
|
|
- LAME MP3 encoder psychoacoustic model - Hydrogenaudio Knowledge Base
|
2026-07-05 00:44:08 +02:00
|
|
|
|
https://wiki.hydrogenaudio.org/index.php?title=LAME
|
2026-08-09 22:17:18 +02:00
|
|
|
|
- "Audio Authentication Using Spectral Analysis" - University of Michigan
|
2026-07-05 00:44:08 +02:00
|
|
|
|
open-access thesis, 2025
|
|
|
|
|
|
https://doi.org/10.7302/28306
|