one canvas encodes the same pixels through toBlob and toDataURL
Check id canvas-toblob-vs-todataurl
What an ordinary browser yields
toBlob and toDataURL decode to byte-identical pixels. One surface encoded twice is one image, on every engine.
What a detector infers
A canvas has three exits — getImageData, toDataURL and toBlob — and a build that perturbs canvas readback has to perturb all three identically or it contradicts itself. This audit already compares the first two. toBlob is the third, and it is worth asking separately because in a real engine it is a different code path from toDataURL: same surface, same encoder, different entry point. That is precisely the kind of seam where a patch gets applied in one place and missed in another, and the result is a canvas that hashes two different ways depending on which method a detector happens to call. The inference available to a detector is unusually strong, which is what makes this row worth its cost. It does not need to know what the correct pixels are, or hold any reference value, or know anything about the machine — it calls two methods on one canvas and observes that the browser gave two answers to one question. No amount of per-site seeding makes that legitimate. The comparison is done on decoded PIXELS rather than on the encoded bytes on purpose: two PNG encoders can legitimately differ in compression choices while painting identical images, and failing a browser for that would be exactly the false positive this audit takes most seriously.
How to resolve it
Apply canvas perturbation once, at a point every exit shares, rather than separately in each encode path. Per-exit seeding makes toDataURL, toBlob and getImageData drift apart, and that disagreement is self-evident to anyone who calls two of them — it needs no reference image to detect.
Read in the wild by
CloakBrowser issues
A stealth-browser tracker records canvas readback differing between exits on one surface, and the resulting two-hashes-from-one-canvas signal.
CloakHQ/CloakBrowser #464, #294, #437 (canvas exit divergence)
CloakHQ/CloakBrowser#464Clearcote (measured)
Confirmed non-accusatory on a stock browser before shipping: the same 40x20 surface encoded through both exits and decoded back gave byte-identical pixels, so the invariant this row asserts is one an unmodified engine actually holds.
stock desktop Chromium on Windows (ANGLE/D3D11, RTX 3070): 0 of 3200 channel bytes differed between toBlob and toDataURL
Every attribution traces to a published artifact. See the sources and their limits.
Nearby checks in Render & GPU
- the clear path and the shader path paint opaque red to the same bytes
webgl-draw-path-invarianceOpaque red can be produced two ways: the fixed-function clear path, and a fragment shader emitting vec4(1,0,0,1). - a mediump claim of fp32 precision is delivered by the arithmetic
shader-mediump-claim-vs-arithmeticgetShaderPrecisionFormat is a claim about float precision; a shader that computes a value only fp32 can represent is the GPU keeping or… - silence renders as exact zero through the Web Audio graph
audio-render-silence-integrityThe other audio checks compare two renders of a sound; this one renders silence and asserts it stayed silent, which catches a different kind… - a constant signal renders at exactly the amplitude it was given
audio-render-dc-integrityThe companion to the silence check below, and the input that one structurally cannot see. - the same audio render reproduces itself
audio-render-reproducibilityAn OfflineAudioContext render involves no output device and no clock. - a browser that implements EME can satisfy its mandatory ClearKey baseline
eme-clearkey-baselineEncrypted Media Extensions is the DRM entry point, and it has one part that cannot be missing. - the WebGL2 element-count limits are reported beside the renderer that claims them
webgl-max-elements-readoutMAX_ELEMENTS_INDICES and MAX_ELEMENTS_VERTICES are hints the driver publishes about how many indices and vertices it draws efficiently in… - the WebGPU preferred canvas format is reported beside the claimed platform
webgpu-canvas-format-readoutnavigator.gpu.getPreferredCanvasFormat() returns the texture byte order the platform's compositor wants to be handed — and that preference…
Clearcote is a browser built for fingerprint coherence
It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.
This audit takes no position on how Clearcote scores on Render & GPU checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.
See the other checks in Render & GPU — the family canvas-toblob-vs-todataurl belongs to.