Understanding Audio Fingerprinting
Audio fingerprinting produces a compact representation of sound characteristics over time. It extracts robust acoustic features—such as pitch, rhythm, and spectral patterns—that remain relatively stable despite minor variations. While distinctive individually, audio fingerprints often exhibit limited uniqueness and are typically combined with other signals for reliable identification.
Observing How Sites Capture Audio Fingerprints
To analyze detection methods, execute the following snippet in a browser's developer console. It renders an offline audio signal and outputs its hashed fingerprint:
const ACClass = window.OfflineAudioContext || window.webkitOfflineAudioContext;
const ctx = new ACClass(1, 5000, 44100);
const osc = ctx.createOscillator();
osc.type = "triangle";
osc.frequency.setValueAtTime(1000, 0);
const comp = ctx.createDynamicsCompressor();
comp.threshold.value = -50;
comp.knee.value = 40;
comp.ratio.value = 12;
comp.reduction.value = 20;
comp.attack.value = 0;
comp.release.value = 0.2;
osc.connect(comp);
comp.connect(ctx.destination);
function computeSHA256(str) {
const encoder = new TextEncoder();
const data = encoder.encode(str);
return crypto.subtle.digest("SHA-256", data).then(buf => {
return Array.from(new Uint8Array(buf))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
});
}
osc.start();
ctx.oncomplete = evt => {
const channel = evt.renderedBuffer.getChannelData(0);
const jsonRepr = JSON.stringify(channel);
computeSHA256(jsonRepr).then(hashed => console.log(hashed));
};
ctx.startRendering();
Typical output might resemble:
e336f0bfce56a91fd1fd0a88530f3bf323ad23cf6155769cc89b09092880cde9
Because audio-derived identifiers lack high entropy, they are usually blended with additional metrics for stronger tracking accuracy.
Modifying Chromium to Emit Variable Audio Fingerprints
Assuming a functional Chromium build environment, adjust the source to vary the sampling rate during offline rendering, producing inconsistent fingerprints across sessions.
Locate third_party/blink/renderer/modules/webaudio/offline_audio_context.cc.
1. Add header:
#include <random>
2. Insert helper near the top:
static int generateRandOffset() {
static std::mt19937 rng(static_cast<unsigned long>(std::time(nullptr)));
std::uniform_int_distribution<int> dist(0, 99);
return dist(rng);
}
3. Modify constructor:
Find:
OfflineAudioContext::OfflineAudioContext(LocalDOMWindow* window,
unsigned number_of_channels,
uint32_t number_of_frames,
float sample_rate,
ExceptionState& exception_state)
: BaseAudioContext(window, kOfflineContext),
total_render_frames_(number_of_frames) {
destination_node_ = OfflineAudioDestinationNode::Create(
this, number_of_channels, number_of_frames, sample_rate);
Initialize();
}
Replace with:
OfflineAudioContext::OfflineAudioContext(LocalDOMWindow* window,
unsigned number_of_channels,
uint32_t number_of_frames,
float sample_rate,
ExceptionState& exception_state)
: BaseAudioContext(window, kOfflineContext),
total_render_frames_(number_of_frames) {
float alteredRate = sample_rate + generateRandOffset();
destination_node_ = OfflineAudioDestinationNode::Create(
this, number_of_channels, number_of_frames, alteredRate);
Initialize();
}
4. Rebuild:
ninja -C out/Default chrome
Each browser launch will now yield a different audio fingerprint due to the fluctuating sample rate. Note that altreing the rate may affect audio playback fidelity.
Testing Fingerprint Variability
Use these services to verify changes: