CreepJS Audio Reports: Rendering and Missing Values
Trace CreepJS audio report fields from OfflineAudioContext rendering through missing, noise and lie branches without treating a checker result as identity.
BotBrowser Team
Want the structured docs for Fingerprint?
This article lives in the editorial library. For step-by-step setup, reference material, and ongoing updates, jump into the docs section.
BotBrowser documents profile-backed AudioContext output and a fixed --bot-noise-seed for reproducible audio values. It cannot guarantee anonymity or site acceptance, and those controls do not make a CreepJS audio report proof of identity. In CreepJS, an absent result, an unavailable field and a flagged comparison have different source-level meanings.
TL;DR
- The pinned collector renders a mono
OfflineAudioContextwith5000frames at44100Hz; microphone capture is not part of that path. binsSampleandcopySampleuse indices4500-4599;sampleSumcovers the larger, overlapping interval4500-4999.- Whole-report absence, a zero displayed as unavailable, and an unsupported copy method are distinct states.
lied, numericnoiseand the known-pattern lookup record implementation-specific checks, not a person's identity or intent.- BotBrowser's profile and seed controls are documented separately; the pinned CreepJS source is not a measurement of those controls or an external service decision.
Contents
- Reading the pinned collector
- Missing values are branch results
- FAQ: CreepJS report fields
- Source-branch decision record
- Practical scenarios
- Reproducibility notes
- Practical conclusion
The diagram shows the source-level decision path. It is not a runtime capture and it does not turn a branch label into a device verdict.
Reading the pinned collector
Revision 10aa6724cd33a1015db1574211890518cd04f0cc first checks whether window.OfflineAudioContext is available. The check happens before graph creation, so a missing report at this point says only that this collector did not enter its render path. It does not establish that audio is absent from the host. BotBrowser documents profile-aware AudioContext output controls for authorized runs; the CreepJS collector does not measure those controls or promise a remote service outcome.
When the constructor exists, the code chooses a mono context with bufferLen = 5000 and 44100 Hz. The rendering helper creates a triangle oscillator at 10000 Hz and connects it to a dynamics compressor. The compressor connects separately to the analyser and the context destination. The sequence matters: graph construction, startRendering(), and the completion callback are separate points where an implementation can expose a different failure. Recording only the final digest loses that distinction.
The completion callback reads several representations of the same render. Frequency-domain data and time-domain data come from different analyser methods. Channel data comes from the rendered AudioBuffer; copyFromChannel is an optional second read path. The collector keeps binsSample and copySample separately rather than pretending that two reads are interchangeable. For comparison, the CreepJS font-table analysis documents a different collector and audio fingerprinting basics explain the broader signal; neither source substitutes for these branch-specific fields.
The sample windows are explicit and overlap. Positions 4500 through 4599 are retained for the small sample record, while sampleSum adds absolute values at positions 4500 through 4999. A changed aggregate with an unchanged small window, or a changed window with an unchanged aggregate, is a different observation. A support record therefore names the field and window instead of treating all audio numbers as interchangeable. Record the source revision, field name, bounds and whether the value was absent; this distinguishes collector-version changes from a difference in the selected read path without retaining every sample.
Missing values are branch results
The renderer has a separate presentation path when the entire offlineAudioContext result is absent. It displays blocked notes for sum, gain, freq, time, trap, unique, data, copy and values. A populated result follows another path: several numeric fields use a truthiness check, so a zero may display as blocked, while a zero time-domain sum displays as unsupported. The displayed note alone cannot distinguish every reason for a zero or absent value. Preserve the underlying field state instead of replacing the note with a successful numeric measurement.
The lied field combines several checks in this revision: recorded lie metadata, unexpected analyser frequencies, the separate fake-audio check, sample-read mismatches and a truthy noise result. noise is numeric: the code uses a nonzero buffer read/write noise factor, or otherwise sums unique values from the first 100 samples of the rendered buffer. A known three-value pattern can separately route a mismatching sampleSum into a lower-entropy branch. Because several causes share one flag, a dashboard should retain the branch detail before assigning a cause.
The comparison should preserve source order: distinguish constructor or render absence from a completed buffer; compare analyser sums with the method that produced them; compare binsSample, copySample, and sampleSum while keeping the sample window; then retain lied, noise, and the branch that explains each flag. This prevents a missing value from being called a hardware difference or a known-pattern mismatch from being called proof of manipulation.
FAQ: CreepJS report fields
Does audio fingerprinting require microphone access?
A: No. It uses the synthesis and processing features of the Web Audio API. No microphone, no permission and no audible sound are involved, and the whole routine runs in software.
Does a blocked field mean the entire collector failed?
A: No. The HTML renderer distinguishes an absent report from a populated report whose individual fields are falsy or unavailable. The source field and rendering condition are needed to interpret the note.
Does the noise seed affect audio playback quality?
A: Playback quality is not measured by the pinned collector. The BotBrowser audio documentation describes supported controls; application compatibility depends on the deployed build and workflow.
How stable is the audio output with a fixed noise seed?
A: The pinned CreepJS source does not test BotBrowser reproducibility. The product documentation describes fixed-seed behavior separately, including the distinction between profile-backed audio and host passthrough.
What happens if I use different noise seeds with the same profile?
A: The pinned collector has no seed-selection branch. Do not infer how BotBrowser maps seeds from this source review.
Why does my result differ between headless and headed runs?
A: The pinned CreepJS collector contains no headed-versus-headless comparison. The BotBrowser audio documentation notes configuration-dependent availability; verify the actual deployment separately before attributing a missing report to mode.
Does a VPN or a proxy change the audio result?
A: A network route is not an input to the pinned oscillator and buffer calculations. That source observation does not establish that two browsing sessions are otherwise identical or that a remote page serves the same code.
Is audio consistency enough on its own?
A: No. Audio is one signal among many. A consistent audio result does not establish the consistency of canvas, WebGL, fonts or navigator properties, nor does it determine a site's decision.
Source-branch decision record
The following record keeps the source observations separate from a runtime result. The revision is the first key, because a later CreepJS change can move a check or rename a field without changing the Web Audio API. The phase is the second key: capability, graph construction, completion, field assembly, comparison, and presentation have different failure meanings. The field is the third key: a missing sampleSum is not the same event as a populated sampleSum with a noise marker.
| Phase | Source observation | Safe statement | Unsafe extension |
|---|---|---|---|
| capability | OfflineAudioContext is absent | This collector returned no audio report | The device has no audio support |
| graph | oscillator, compressor and analyser are connected | These nodes were selected for the render | Every browser uses the same graph |
| completion | the completion handler receives an AudioBuffer | The selected render finished far enough to read the buffer | The output proves a physical audio path |
| field assembly | sums and samples are calculated | These named values are derived from the buffer and analyser | The digest is a hardware identifier |
| comparison | a trap, lie property or known pattern differs | One branch recorded an unexpected comparison | A person or service is deceptive |
| presentation | blocked notes or highlighted differences are rendered | The UI preserved an unavailable or divergent state | The UI is a browser standard |
Each row maps one report state to a bounded follow-up. If the constructor is absent, inspect feature exposure and the page context. If the constructor is present but completion never arrives, inspect lifecycle errors and resource limits. If the buffer arrives but a copy field is undefined, inspect copyFromChannel support. If a known-pattern mismatch appears, compare the three-value key before changing a profile. A ticket can preserve the revision, phase, field and branch while omitting raw samples; that is enough to reproduce the source interpretation and does not create a reusable waveform record.
The source also makes a useful distinction between getChannelData and copyFromChannel. Both can read channel zero, but the collector samples each path independently and keeps binsSample and copySample as different values. A comparison that agrees is evidence that these two reads agreed for the selected render. A comparison that differs is evidence of a divergence in that path. It is not automatically evidence of tampering, because the source does not enumerate every implementation or wrapper that can affect a read.
The analyser fields have their own limits. floatFrequencyDataSum describes the frequency array, while floatTimeDomainDataSum describes a different array. The latter array is allocated before the optional time-domain method is checked; without that method it remains zero-filled. Its zero sum is then displayed as unsupported. Equal sums do not imply equal arrays, and unequal sums do not identify a cause. A support note should name both the method's availability and the array-derived field, not simply say “audio changed.”
Compressor reduction is another separate input. The source reads the reduction property using a compatibility path because implementations can expose a value or an object with a value. That compatibility branch explains why the field is normalized before the known-pattern key is built. A later browser version could change the exposed property shape without changing the intended compressor operation. The record should report the observed property path and revision rather than promise cross-engine equality.
The sample window is not evidence of a whole track. The collector chooses indices 4500 through 4599 for each one-hundred-value sample and 4500 through 4999 for the aggregate. The first interval is contained in the second. A change within either interval can affect its sum, but aggregation can hide offsetting changes in individual magnitudes. A debugger who compares only the aggregate can miss a localized change; storing every sample may retain more information than the support question needs. Keep the bounds and a coarse comparison outcome together.
The graph render and hasFakeAudio() are awaited concurrently, but they return different evidence. The former supplies the buffer and analyser fields. The latter uses a separate 100-frame context at 44100 Hz with a zero-frequency oscillator and checks the channel data. A rejection of that separate check is mapped to false by its catch handler, so false does not prove that the check completed successfully. A true result sets lied through its own branch. This is not a comparison of two matched renders for repeatability.
Two known-value lookups have different jobs. Inside the collector, a key combines compressor reduction and the two analyser sums. If that key exists but its allowed values exclude sampleSum, the code sets a lower-entropy flag and records a suspicious-data note. In the HTML renderer, KnownAudio is keyed only by compressor reduction and controls how the sum is presented. Neither table claims to enumerate every valid browser, and this file does not document a complete sampling methodology for either table. “Known” means present in that implementation's lookup, not globally normal; “unexpected” does not establish malicious intent.
Practical scenarios
A report is entirely blocked. Start by checking whether the constructor branch returned early or the outer error handler returned no result. These are explicit paths in the source; a screenshot of the blocked panel does not reveal which one ran. Preserve any applicable error record, the page context and the source revision. Do not add a fallback number merely to make a dashboard complete. The absence is an observation that should remain visible.
The report has numbers but lied is true. Read the branch message. If it names an analyser cardinality, compare the analyser method and expected silence assumption. If it names a property lie, inspect the exact property and realm. If it names fake audio, retain that as the collector's label rather than a claim about the page. If it names a sample comparison, compare the window definition and copy method. The next action depends on the message.
The report has a nonzero noise field. Keep its numeric type distinct from the lied boolean. The code sets lied when noise is truthy, but the returned fields are not aliases. A digest-only alert loses the origin of the flag. A privacy-minimized record can preserve a coarse “zero/nonzero” noise state, the source revision and the affected field names without storing the raw sample array; label that coarse state as a derived record rather than the original numeric field.
A known pattern mismatch appears after a browser update. Compare the browser build and the CreepJS revision first. Then compare the three-value pattern and sample sum. If the source changed the known table, the same browser may receive a different classification. If only the browser changed, the result is an implementation comparison. Neither outcome proves that the new build is less private or that the old build was authentic.
A copy field is unavailable while channel data exists. Check the copyFromChannel support condition before interpreting a mismatch. The collector reads channel zero through separate APIs, but the returned copySample becomes [undefined] when the method is not supported. That is different from two supported reads producing different samples. The mismatch branch also checks support before setting lied. A record that collapses both cases into “copy failed” removes the condition needed to interpret the flag.
A support team asks for a screenshot. A screenshot can show the presentation branch, but it cannot prove the hidden source fields or the cause of a branch. Pair it with the source revision, field names, and a small decision record. Avoid screenshots that expose account identifiers, complete raw values, or unrelated pages. The report's purpose is to route a compatibility question, not to create a reusable fingerprint.
A product team wants one pass/fail metric. Keep the branch-level record and derive a product metric only after defining the decision. A missing constructor might be acceptable for a feature that does not need offline audio; it might be a release blocker for an audio editor. A lied flag might require investigation without failing a user. The CreepJS report cannot decide that product policy. It supplies observations for the owner to interpret.
Reproducibility notes
Source review is reproducible when another reader can fetch the same revision, inspect the same line ranges, and obtain the same branch table. Runtime reproduction has a higher burden: it must also pin the browser build, operating system, profile lifecycle, page origin, permissions, and fixture bundle. A run record that omits those inputs cannot distinguish a browser change from a context change. The claims here are limited to source evidence; no runtime output is asserted.
When a runtime record is authorized, capture only the minimum fields required for the question. A useful JSON shape contains sourceRevision, fixture, browserBuild, os, context, testedOn, phase, field, branch, and interpretation. Keep interpretation bounded: “copySample differs from binsSample” is stronger than “audio is fake,” because the former is an observation and the latter is an unsupported cause. Store the raw values in a restricted investigation only when the data owner has approved the retention.
Technical identifiers such as OfflineAudioContext, sampleSum, copyFromChannel, lied and noise are distinct keys in the source. Preserve those names in diagnostic records rather than combining them into a single “audio status.” For BotBrowser configuration, the noise-seed reproducibility guide separates profile, build and seed inputs. Also record whether --bot-audio uses profile or real: the documentation states that host passthrough does not apply profile audio values or audio noise. A fixed seed and an unchanged report cannot establish that the same control path was selected unless the launch configuration is known.
Sources
- CreepJS
src/audio/index.tsat revision10aa6724 - MDN: OfflineAudioContext
- MDN: OfflineAudioContext.startRendering
- MDN: AudioBuffer.getChannelData
- BotBrowser audio documentation
Practical conclusion
Use the pinned source to classify where the collector stopped and which fields it read. Keep unavailable output, analyser values, sample windows, lied, and noise separate. The source adds a branch-level diagnostic to the broader BotBrowser audio documentation; it does not provide a runtime result or establish identity, anonymity, or an external service's decision. Audio remains a single-surface report; the CreepJS prediction aggregation analysis follows a separate display path and explains the limits of unknown and pending states. For related surfaces, compare CreepJS font-table analysis and WebGL fingerprinting in their own evidence boundaries.
This article is part of the guide: What Is a Browser Fingerprint?
Related Articles
Audio Fingerprinting Explained: How AudioContext Tracks You
FingerprintDeterministic Mode: Reproducible Browser Fingerprints
FingerprintCreepJS Math Signals: Read the Source Fields
FingerprintNoise Seed Reproducibility for Repeatable Fingerprint Runs
FingerprintWeb Audio API Access and Practical Audio Workflows
FingerprintCreepJS Intl Report: Fields and Missing Values
Take BotBrowser from research to production
The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.