diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml
new file mode 100644
index 000000000..2543d135d
--- /dev/null
+++ b/.github/workflows/codspeed.yml
@@ -0,0 +1,38 @@
+name: CodSpeed
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+ # `workflow_dispatch` allows CodSpeed to trigger backtest
+ # performance analysis in order to generate initial data.
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ id-token: write # for OpenID Connect authentication with CodSpeed
+
+jobs:
+ benchmarks:
+ name: Run benchmarks
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.12'
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r test/benchmarks/requirements.txt
+
+ - name: Run benchmarks
+ uses: CodSpeedHQ/action@v4
+ with:
+ mode: simulation
+ run: pytest test/benchmarks/ --codspeed
diff --git a/README.md b/README.md
index 44e1bce50..f88ba99c6 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@


+[](https://app.codspeed.io/NeptuneHub/AudioMuse-AI?utm_source=badge)
⭐ Leave a star on this project: One shines alone; together, they make it visible and keep it alive.
diff --git a/test/benchmarks/__init__.py b/test/benchmarks/__init__.py
new file mode 100644
index 000000000..23476669a
--- /dev/null
+++ b/test/benchmarks/__init__.py
@@ -0,0 +1,9 @@
+# AudioMuse-AI - https://github.com/NeptuneHub/AudioMuse-AI
+# Copyright (C) 2025 NeptuneHub
+# SPDX-License-Identifier: AGPL-3.0-only
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License v3.0. See the LICENSE file
+# in the project root or
+
+"""Package marker for the AudioMuse-AI CodSpeed performance benchmarks."""
diff --git a/test/benchmarks/requirements.txt b/test/benchmarks/requirements.txt
new file mode 100644
index 000000000..5da6f9590
--- /dev/null
+++ b/test/benchmarks/requirements.txt
@@ -0,0 +1,8 @@
+# Dependencies for running the CodSpeed performance benchmarks in test/benchmarks/.
+# These benchmarks target self-contained, dependency-light hot paths, so they
+# only need numpy (for the sanitization helpers) plus the CodSpeed pytest plugin.
+# numpy is pinned to match test/requirements.txt and requirements/common.txt.
+
+numpy==1.26.4
+pytest>=7.0.0
+pytest-codspeed
diff --git a/test/benchmarks/test_bench_playlist_ordering.py b/test/benchmarks/test_bench_playlist_ordering.py
new file mode 100644
index 000000000..d3449d02a
--- /dev/null
+++ b/test/benchmarks/test_bench_playlist_ordering.py
@@ -0,0 +1,75 @@
+# AudioMuse-AI - https://github.com/NeptuneHub/AudioMuse-AI
+# Copyright (C) 2025 NeptuneHub
+# SPDX-License-Identifier: AGPL-3.0-only
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License v3.0. See the LICENSE file
+# in the project root or
+
+"""CodSpeed performance benchmarks for the playlist sonic-ordering distances.
+
+Measures the pure distance helpers that drive the greedy nearest-neighbour walk
+used when sequencing playlists. These run once per candidate pair, so their cost
+scales quadratically with playlist size and is worth tracking.
+
+Main Features:
+* Benchmarks the circle-of-fifths key distance across many key/scale pairs.
+* Benchmarks the composite tempo/energy/key distance over a realistic song set.
+"""
+
+import pytest
+
+from test.unit.conftest import _import_module
+
+
+playlist_ordering = _import_module(
+ 'tasks.playlist_ordering', 'tasks/playlist_ordering.py'
+)
+
+KEYS = ['C', 'G', 'D', 'A', 'E', 'B', 'F#', 'Db', 'Ab', 'Eb', 'Bb', 'F', None, 'XYZ']
+SCALES = ['major', 'minor', None]
+
+KEY_PAIRS = [
+ (k1, s1, k2, s2)
+ for k1 in KEYS
+ for s1 in SCALES
+ for k2 in KEYS
+ for s2 in SCALES
+]
+
+SONGS = [
+ {
+ 'tempo': 60 + (i * 7) % 140,
+ 'energy': (i % 20) / 20.0,
+ 'key': KEYS[i % len(KEYS)],
+ 'scale': SCALES[i % len(SCALES)],
+ }
+ for i in range(200)
+]
+
+
+def _run_key_distances():
+ total = 0.0
+ for k1, s1, k2, s2 in KEY_PAIRS:
+ total += playlist_ordering._key_distance(k1, s1, k2, s2)
+ return total
+
+
+def _run_composite_distances():
+ total = 0.0
+ for a in SONGS:
+ for b in SONGS:
+ total += playlist_ordering._composite_distance(a, b)
+ return total
+
+
+@pytest.mark.benchmark
+def test_bench_key_distance(benchmark):
+ result = benchmark(_run_key_distances)
+ assert result >= 0.0
+
+
+@pytest.mark.benchmark
+def test_bench_composite_distance(benchmark):
+ result = benchmark(_run_composite_distances)
+ assert result >= 0.0
diff --git a/test/benchmarks/test_bench_sanitization.py b/test/benchmarks/test_bench_sanitization.py
new file mode 100644
index 000000000..0b1e475da
--- /dev/null
+++ b/test/benchmarks/test_bench_sanitization.py
@@ -0,0 +1,75 @@
+# AudioMuse-AI - https://github.com/NeptuneHub/AudioMuse-AI
+# Copyright (C) 2025 NeptuneHub
+# SPDX-License-Identifier: AGPL-3.0-only
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License v3.0. See the LICENSE file
+# in the project root or
+
+"""CodSpeed performance benchmarks for the sanitization helpers.
+
+Measures the string and JSON sanitization paths that run on every database
+write and API response, using realistic payload sizes so the benchmarks track
+the cost of the regex stripping and numpy conversion hot paths.
+
+Main Features:
+* Benchmarks NUL/control-character stripping on plain strings.
+* Benchmarks nested-JSON sanitization and numpy-to-native conversion.
+"""
+
+import numpy as np
+import pytest
+
+import sanitization
+
+
+DIRTY_STRING = (
+ "Song\x00Title \x01with \x02control\x1f chars and a long tail "
+ "of unicode text \u00e9\u00e8\u00ea " * 40
+)
+
+NESTED_JSON = {
+ "tracks": [
+ {
+ "item_id": f"track-{i}",
+ "title": f"Title\x00 {i}",
+ "author": f"Artist\x1f {i}",
+ "moods": {"happy": 0.8, "energetic": 0.6, "calm": 0.2},
+ "tags": [f"tag\x00{j}" for j in range(5)],
+ }
+ for i in range(50)
+ ],
+ "meta": {"note": "clean\x00note", "count": 50},
+}
+
+NUMPY_PAYLOAD = {
+ "embedding": np.random.rand(200),
+ "scores": [np.float64(v) for v in np.random.rand(50)],
+ "counts": {"a": np.int64(3), "b": np.int32(7)},
+ "flag": np.bool_(True),
+ "matrix": np.random.rand(20, 20),
+}
+
+
+@pytest.mark.benchmark
+def test_bench_sanitize_string_for_db(benchmark):
+ result = benchmark(sanitization.sanitize_string_for_db, DIRTY_STRING)
+ assert "\x00" not in result
+
+
+@pytest.mark.benchmark
+def test_bench_sanitize_db_field(benchmark):
+ result = benchmark(sanitization.sanitize_db_field, DIRTY_STRING, 1000, "title")
+ assert "\x00" not in result
+
+
+@pytest.mark.benchmark
+def test_bench_sanitize_json_for_db(benchmark):
+ result = benchmark(sanitization.sanitize_json_for_db, NESTED_JSON)
+ assert result["meta"]["count"] == 50
+
+
+@pytest.mark.benchmark
+def test_bench_sanitize_for_json(benchmark):
+ result = benchmark(sanitization.sanitize_for_json, NUMPY_PAYLOAD)
+ assert isinstance(result["embedding"], list)