Back to Knowledge Hub
Fingerprint

Deterministic Mode: Reproducible Browser Fingerprints

How a fixed noise seed keeps Canvas, WebGL, Audio, and text-metric noise reproducible across sessions, and how to check that one seed repeats while another seed changes the output.

BotBrowser Team

Documentation

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.

Why reproducible noise matters

Fingerprint noise has two jobs that pull in opposite directions. Small variations in Canvas, WebGL, Audio, and text measurements keep a browser from presenting the raw output of one machine. A test or a long-running workflow needs the opposite: the same output when you run it again tomorrow. If every launch draws fresh noise, you cannot tell whether a changed result came from your configuration, from the page under test, or from the noise itself.

A noise seed settles that conflict. It is a single integer that initializes a pseudo-random number generator, so the sequence of noise values is fixed by the seed. Two launches that use the same seed apply the same variation to the same drawing or measurement, and a different seed applies a different variation. You decide when the output should repeat and when it should change.

Privacy engineers and testers usually reach for a fixed seed in four situations:

  • Session continuity. Returning to a service across several launches while presenting the same Canvas, WebGL, Audio, and text-metric output each time.
  • Distinct stable identities. Giving separate accounts or test users their own noise pattern that does not move between launches.
  • Configuration checks. Confirming that a profile and a set of flags produce the output you expect, and that changing the seed produces something different.
  • Reproducible experiments. Removing fingerprint noise as a variable when you compare two builds, two pages, or two profiles.

A noise seed feeds a pseudo-random generator that drives Canvas, WebGL, Audio, and text-metric noise, so the same build, profile, and seed repeat the same output

The sections below follow the seed from the surfaces it touches, through seed selection and launch arguments, to a verification routine and the limits you should plan around. The reference material is the public noise seed reproducibility documentation, and the Canvas behavior is described in the MDN Canvas API reference listed under sources.

What the seed controls and what it leaves alone

The --bot-noise-seed flag applies to the surfaces where BotBrowser adds noise:

  • Canvas 2D. Shapes, text, and pixel readback through toDataURL(), toBlob(), and getImageData().
  • WebGL and WebGPU. Rendered output that a page reads back from a drawing surface.
  • Audio. Sample values produced by audio processing, for example an OfflineAudioContext render.
  • Text metrics. Measurements returned by measureText(), getBoundingClientRect(), and getClientRects().

The seed repeats noise for the same content. A page that draws the same scene under seed 42 in two sessions reads back the same pixels. The same scene under seed 43 reads back different pixels that are just as stable. This is why a verification page should draw a fixed scene: if the content changes between runs, the output changes for reasons that have nothing to do with the seed.

The seed does not touch values that come from the profile. Navigator properties, screen dimensions, and the user agent string are read from the profile and stay the same on every launch whatever the seed is. Storage, cookies, and session state are a separate matter as well. They live in the directory set by --user-data-dir and in values loaded with --bot-cookies, so a seed does not restore a login or remember a previous visit.

Each noise channel also has its own switch in the command-line reference: --bot-noise-canvas, --bot-noise-webgl-image, --bot-noise-audio-context, --bot-noise-client-rects, and --bot-noise-text-rects. They accept true or false. The seed decides what the noise looks like, and the switches decide which surfaces receive noise at all, so the two controls answer different questions.

The value itself has two rules. The valid range is 1 to 4294967295, the largest unsigned 32-bit integer. A value of 0 behaves differently: it keeps noise active with the profile defaults but without deterministic seeding, so output is not expected to repeat between sessions. If you need repeatability, choose a positive integer and pass it on every launch.

Timing is controlled separately. --bot-time-seed takes an integer from 1 to 4294967295 for a reproducible execution timing policy, and 0 disables it. --bot-time-scale takes a float below 1.0 to scale the timing policy. --bot-stack-seed accepts profile, real, or a positive integer for stack depth behavior. These are independent controls, so a noise seed alone does not make timing repeat, and a time seed does not change Canvas output. When a test needs both rendering noise and timing to repeat, set both values on purpose and record both.

Choosing seeds for stable identities

A practical way to think about the pair is that the profile defines the device class and the seed defines the noise pattern within that class. Profile A with seed 100 is one stable, reproducible fingerprint. Profile A with seed 200 shares the same profile values but has a different noise pattern. Profile B with seed 100 has a different base and therefore different output, even though the seed value is the same. A seed never makes two different profiles produce the same fingerprint, because the base values still differ.

A small plan for three sessions might look like this:

  • Session one: profile A with seed 100, for the first account or test user.
  • Session two: profile A with seed 200, for a second account that should keep its own noise pattern.
  • Session three: profile B with seed 100, for a different device class that happens to reuse a seed value.

A few habits keep that plan reliable over time. Generate each seed once, then store it next to the account or test case it belongs to, because the seed cannot be recovered from the output later. Do not derive a seed from something that changes, such as a timestamp or a counter that resets, since that quietly turns a deterministic setup back into a different value on every launch. Do not reuse one seed for sessions that should keep separate noise patterns. And keep the seed assignment in the same place as the profile file name, so that a future you can rebuild the exact pair.

When one browser process needs more than one identity, per-context fingerprinting (ENT Tier3) lets each BrowserContext carry its own --bot-noise-seed. The documentation shows the flags being set for a context before its first page is created, so the renderer starts with the correct values. A second context with a different seed then produces a different but equally reproducible pattern.

Seeds are ordinary configuration values. They do not need to be sequential, and any positive integer in the valid range works. They are not credentials either: anyone who holds the same build, the same profile, and the same seed can reproduce the same noise pattern, which is the point of a reproducible setup. Treat them like other test configuration, version them with the rest of the setup, and keep them out of places where an accidental reuse is likely.

Keep the scope of a seed in mind. Different seeds give different noise patterns on top of the same profile values, which is not the same as unrelated browsers. Cookies, storage, the network route, and the behavior of the person or script using the browser are separate signals, and a seed does not manage any of them.

Launching with a fixed seed

The smallest deterministic launch passes a profile and a seed. A fresh user data directory keeps old browser state out of the comparison:

chromium-browser \
  --bot-profile="path/to/profile.enc" \
  --bot-noise-seed=42 \
  --user-data-dir="$(mktemp -d)"

When timing should repeat as well, add the independent timing and stack depth controls with their own values:

chromium-browser \
  --bot-profile="path/to/profile.enc" \
  --bot-noise-seed=42 \
  --bot-time-seed=42 \
  --bot-stack-seed=profile

Two concurrent sessions that should keep different identities use different seeds and separate user data directories. The seed and the directory are different controls: the first sets the noise pattern, the second holds cookies and storage.

chromium-browser --bot-profile="path/to/profile.enc" --bot-noise-seed=1001 --user-data-dir="tmp/session-a" &
chromium-browser --bot-profile="path/to/profile.enc" --bot-noise-seed=1002 --user-data-dir="tmp/session-b" &

Browser automation frameworks pass the same flags as launch arguments next to the executable path of the BotBrowser build. Set them in the launch arguments rather than after a page has opened. The seed does not change how the framework drives the page.

Sometimes you need deterministic output for some surfaces and the host's own rendering for others. The per-surface switches accept true or false, and a surface that you turn off is no longer governed by the seed:

chromium-browser \
  --bot-profile="path/to/profile.enc" \
  --bot-noise-seed=42 \
  --bot-noise-canvas=false \
  --bot-noise-audio-context=false

Treat that last form as a diagnostic step. If a test result stops repeating, turning one channel off at a time shows which surface is responsible. Switch the channels back on before you rely on the setup, and keep the final launch command in your notes so the same arguments can be reused. The goal is a single command line that you can paste into a second session and expect to behave the same way.

Verifying reproducibility

Verification only means something if the page under test is stable. Use a page that you control, or a fingerprint test page you trust to draw the same scene every time. It should draw a fixed Canvas scene, read back a fixed WebGL scene, render a fixed offline audio buffer, and measure a fixed string. Record the values it reports, or a short digest of each one, so comparisons are exact. Run the page the same way each time, with the same window size and the same load order, so that the only deliberate difference between runs is the seed.

  1. Launch with a profile and --bot-noise-seed=42, load the page, and record the Canvas, WebGL, Audio, and text-metric values.
  2. Close the browser, launch again with the same profile and seed, and load the same page. Every recorded value should match the first run.
  3. Change the seed to --bot-noise-seed=43 and run the page a third time. The values should differ from the first run, which confirms that the seed is what shapes the output.
  4. Launch twice with --bot-noise-seed=0. Output is expected to vary between those sessions, because 0 keeps noise active without deterministic seeding.
  5. Save the BotBrowser build, the profile file, the host operating system, and the complete argument list next to the results.

The last step is the one people skip, and it is the one that makes a later mismatch easy to explain. A result is only comparable to another result when the build, the profile, the seed, and the launch arguments are the same.

When a second session does not match the first, work through the cheap explanations before suspecting the seed. Check that the profile path and the profile version are the same, that no framework is injecting extra arguments, and that the page draws the same content each time. Compare the BotBrowser build, the host operating system target, and the full argument list. The documentation also recommends keeping the proxy, the locale and timezone, and the machine load stable while you compare runs, because those can change what a page does even when the seed has not changed.

Repeat the routine whenever one of the inputs changes: upgrading BotBrowser, replacing a profile file, moving to a different machine, or editing the launch arguments. A short re-check after each change is cheaper than discovering drift in the middle of a long test run, and it keeps the cause of any difference narrow, because only one input moved.

A record that lets you rebuild a session needs only a few fields for each seed:

  • The seed value and the account or test case it belongs to.
  • The profile file name and its version.
  • The BotBrowser build you used.
  • The host operating system target and the kind of machine.
  • The complete launch argument list, including any timing or stack seed.
  • The values the verification page reported, with the date of the run.

With that record in place, an upgrade becomes a comparison instead of a surprise. Run the verification page on the new build with the stored seed and compare it with the stored values. If they match, carry on. If they differ, treat the new values as a new baseline, write down the build that produced them, and do not edit the seed in an attempt to recover the old output, because the seed was never the part that changed.

Sometimes a new noise pattern is exactly what you want, for example when you retire a test identity and start another. Change the seed on purpose, update the record in the same step, and keep the old entry with its build number. The old seed remains a valid description of what that build produced for that profile.

Verification of this kind answers a narrow question: does this build, with this profile and this seed, repeat its noise? It does not tell you how any particular site reacts to the fingerprint. Keep those two questions apart in your notes.

Limits to plan around

Reproducibility is scoped to the same BotBrowser build, the same profile, and the same --bot-noise-seed value. The noise algorithm can change between major versions, so a seed that matched on one major version should be verified again after you upgrade. A different machine, a different host operating system target, or different launch arguments can also change results. Treat a cross-machine or cross-version match as something to confirm with the routine above rather than something to assume.

A stable fingerprint is also a trade-off. Presenting the same output on every visit is what makes session continuity work, and it is the same property that lets repeated visits be recognized as repeated. If you want separate sessions to carry unrelated noise patterns, give them different seeds, and remember that the noise pattern is only one of the signals a service can see. A seed does not make a fingerprint unlinkable, and nothing here is a statement about how any site will respond to it.

It also helps to be precise about what a passing verification lets you say. It lets you say that a given build, profile, and seed repeated the same Canvas, WebGL, Audio, and text-metric output on a given machine, and that a different seed changed it. It does not let you say that the same result will hold on every machine, after every upgrade, or on every page. Write results in the first form, and the record stays accurate when someone reads it months later.

Seed 0 deserves a final mention because it is easy to select by accident. It keeps noise active with the profile defaults, which is useful when you want variation, but it is not a deterministic setting. If your verification shows output changing between launches, check first that the seed on the command line is a positive integer and that your launcher or framework is passing it through.

BotBrowser supports --bot-noise-seed (ENT Tier2), an integer from 1 to 4294967295 that seeds the noise on Canvas, WebGL, Audio, and text metrics, so the same profile and seed repeat the same fingerprint in a session-continuity or verification test, while a different seed gives a distinct stable identity. BotBrowser cannot guarantee identical output across different builds, profiles, or changed launch arguments, cannot make a fingerprint unlinkable or accepted by a site, and does not replace storage or cookie management, proxy and IP choices, or the site's own checks.

For background on the surfaces involved, see What Is Browser Fingerprinting, Canvas Fingerprinting, Audio Fingerprint Protection, and the shorter Noise Seed Reproducibility overview.

Sources

#Deterministic#Noise-Seed#Reproducibility#Fingerprinting#Privacy

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.