Back to Knowledge Hub
Fingerprint

CreepJS SVG Report Metrics: Fields and Limits

Trace CreepJS SVG report fields to a pinned collector, distinguish lossy text-length grouping from geometry, and read blocked states without inferring identity.

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.

SVG text measurements come from browser SVG and DOM APIs, not a standalone device label. BotBrowser can configure profile-backed fonts for compatibility work, but this source walkthrough does not run CreepJS or promise identical SVG measurements across platforms.

CreepJS SVG collector flow from its off-screen fixture through field aggregation and the shift check

TL;DR

The pinned CreepJS SVG collector builds an off-screen text fixture, reduces an SVG bounding box, keeps one emoji for each distinct getComputedTextLength() string, and compares measurements before a shift-svg class toggle, while the class is present, and after it is removed. Its output fields describe that implementation's measurements and branches. They do not identify an operating system, a device, or a person. In particular, svgrectSystemSum is a lossy sum over distinct length strings; the source does not define it as a system metric.

Contents

Collector setup and scope

BotBrowser documents profile-backed font bundles and fallback controls, but those controls do not make a CreepJS result an operating-system or device identifier. The useful question here is narrower: which operations in the pinned src/svg/index.ts feed the fields that its renderer calls SVGRect?

The collector starts by combining several existing lie flags, including properties associated with SVGRect, SVGRectElement.getBBox(), and three SVGTextContentElement methods. It then chooses a document reference, appends a div, and patches in an SVG containing one <text> node for each item in EMOJIS. The fixture is positioned off-screen; its text nodes use a configured font family and a 200px font size. The collector adds and later removes shift-svg around two reads. The base and shifted CSS selectors specify the same scale value; the shifted selector adds !important, so these declarations do not establish a changed numeric scale.

These are source facts, not measured results. The source does not establish that every browser renders the fixture, that every configured font is installed, or that the resulting values are unique to a platform. A source map is therefore more useful than a guessed operating-system label: each table row below connects an output field to a specific operation and its information loss.

The collector waits on queueEvent(timer) before and during measurement. That establishes control flow in this pinned implementation; it does not establish that a report was produced in a live browser session. No runtime measurement or CreepJS page execution is claimed here.

Where the seven report fields come from

The returned data object contains seven named properties. The renderer divides the displayed bBox, char, subs, and text values by 10,000, and maps falsy inputs, including zero, to BLOCKED. A falsy svgrectSystemSum is shown as UNSUPPORTED. The emojiSet display uses formatEmojiSet(); its help title includes a short hash and the representative list.

FieldSource operationTransformation or boundary
bBoxsvgBox.getBBox()reduceToObject() keeps non-function properties; getObjectSum() adds their absolute values.
extentOfCharsvgElems[0].getExtentOfChar(EMOJIS[0])The first configured emoji is passed as the argument. The source alone does not justify saying the value is the full emoji's extent.
subStringLengthsvgElems[0].getSubStringLength(0, 10)The first text node is queried with start 0 and length 10; this is a call shape, not a cross-browser result.
computedTextLengthsvgElems[0].getComputedTextLength()One measurement of the first node is stored separately from the representative set.
emojiSetEach node's getComputedTextLength()One emoji is retained for each previously unseen stringified length.
svgrectSystemSumThe pattern set of those stringsNumeric pieces of the distinct strings are summed, then the aggregate is multiplied by 0.00001.
liedExisting lie flags plus the shift predicateStarts from selected prior flags and can become true when the shift/restore comparison differs.

bBox is not a serialized rectangle. reduceToObject() enumerates keys from nativeObj.__proto__, drops method-valued members, and returns an object of the remaining properties. getObjectSum() then takes the absolute value of each property and adds the values. This means a reader should not reconstruct x, y, width, and height from bBox as though the report preserved a rectangle tuple. The output is a scalar derived from the rectangle-like object.

The method names also identify different inputs. getBBox() supplies a bounding box for the SVG graphics element. getComputedTextLength() supplies a text-content length. getSubStringLength() takes a range-like pair of indices. getExtentOfChar() is invoked with the EMOJIS[0] string in the source. Without separately establishing how that argument is converted by the API, preserve the literal call and avoid assigning whole-emoji semantics to its output.

This is why the field-to-operation map is the main artifact: it makes source shape, transformation, and limit visible together. A label such as “SVG fingerprint” would erase those distinctions and imply more than the code proves.

Why emojiSet loses multiplicity

The collector creates pattern = new Set() and a list of the fixture's .svgrect-emoji elements. For each element at index i, it reads getComputedTextLength(), converts the result to a string, and checks whether that string is already in pattern. Only an unseen string is added to pattern, and only then is EMOJIS[i] added to emojiSet.

That sequence matters. If several emoji produce the same stringified length, emojiSet retains the first encountered emoji for that value. It does not retain every input emoji, a frequency count, a font-file name, a fallback chain, or a list of supported glyphs. A set of representatives is not an inventory.

For example, a purely illustrative input sequence of length strings "42", "42", and "57" yields two entries in pattern, not three. The selected emoji would be the first member associated with "42" and the first associated with "57". Those numbers are arithmetic examples, not CreepJS output. Because duplicate lengths disappear, this transformation cannot recover how many input emoji shared a value.

The representative policy is separate from the approach in CreepJS emoji metric boundaries, which traces Canvas TextMetrics tuples and their own representative grouping. The result is also different from a font inventory: document.fonts.check() or a profile's font list answers a different question from an SVG text node's computed length. The BotBrowser font documentation describes profile font bundles, fallback, and rendering controls; it does not turn a measured text length into a list of installed font files.

No frequency survives the Set operation. If ten inputs share a length and one other input differs, the later sum sees two unique strings. It cannot tell whether the first group contained two emoji or two hundred. Any explanation that calls emojiSet a count, coverage percentage, or exhaustive support list would contradict the collector's data path.

What svgrectSystemSum computes and discards

The variable name includes the word System, but the implementation defines the calculation more narrowly than that label suggests. For each string in pattern, the code splits on commas, converts each part with unary +, substitutes 0 for a falsey numeric result, and adds the parts. It then adds those per-string totals and multiplies the final total by 0.00001.

Written as source logic, the operation is:

sum(pattern.map(s => sum(s.split(',').map(part => +part || 0)))) * 0.00001

The input to the calculation is the set of distinct getComputedTextLength() strings, not all original nodes and not a seven-property geometry tuple. Because repeated strings were discarded earlier, the sum is already lossy before the comma split. The scale factor compresses its magnitude; it does not restore the omitted structure or identify a platform.

Consider an explicitly hypothetical set {"42", "57"}. Its two numeric parts sum to 99; multiplying by 0.00001 gives 0.00099. If ten fixture nodes had produced "42" and one had produced "57", the same distinct set would produce the same arithmetic result. This is an illustrative calculation, not browser output or a CreepJS sample.

The computation also does not preserve which API returned each value, each original emoji index, the number of duplicates, or a per-font explanation. Do not rename it in prose as a “system score,” “OS sum,” or “font fingerprint.” The defensible wording is “a scaled sum of numeric pieces from distinct stringified text lengths, as implemented in this pinned source.”

The distinction from Canvas is important. The Text measurement fingerprinting guide covers Canvas measureText() and TextMetrics, where a reader sees a different API and a different set of returned dimensions. The SVG collector does not simply repeat those Canvas fields under another name. Likewise, ClientRects consistency checks concern Element and Range rectangle fragments; they do not own SVG text-content lengths or this collector's representative set.

The shift check and lied state

After building the representative set, the collector selects svgElems[0] and reads its initial getComputedTextLength(). It adds shift-svg, reads the value again, removes the class, and reads the restored value. The comparison is:

initial - shifted != unshifted - shifted;

If that expression is true, the collector sets lied = true and calls documentLie('SVGTextContentElement.getComputedTextLength', 'failed unshift calculation'). This says what branch is taken by the source. It does not specify a universal browser invariant, prove why two numbers differed, or establish that an external service would label the environment automated.

The lied field is not initialized only by this one check. Before the test, it is seeded from selected entries in lieProps, including SVG rectangle properties, getBBox, getExtentOfChar, getSubStringLength, and getComputedTextLength. A false value therefore means only that the accumulated predicate remained false for the inputs and code path represented by this implementation. It does not certify that every SVG behavior is native, unmodified, or identical across platforms.

A useful state table keeps observation separate from explanation:

Source-visible stateWhat the code establishesWhat it does not establish
Initial, shifted, and restored values satisfy the comparisonThis predicate did not set lied on this path.A normal profile, a particular OS, or a clean browser.
The comparison differsThe collector sets lied and records the named documentLie entry.The cause, intent, or downstream verdict.
An earlier selected lieProps entry is trueThe initial lied value can already be true.Which specific change caused it unless the relevant source evidence is retained.
The call throwsThe catch path logs passed: false and returns without the data object.That SVG is unsupported, blocked by policy, or missing for one particular reason.

The table is a source-reading aid, not a test result. It avoids turning a branch name into a causal conclusion.

Missing, blocked, and error paths

The collector's result builder has a separate branch for !fp.svg. In that branch, the rendered card uses the SVGRect heading and blocked placeholders for bBox, char, subs, text, and the summary block. The display does not explain the cause of the absent fp.svg value. A blocked placeholder should not be rewritten as “SVG disabled,” “font unavailable,” or “bot detected” without independent evidence.

There is also a try/catch around collection. On an exception, the source calls logTestResult({ test: 'svg', passed: false }), passes the error to captureError, and returns. This is different from a successfully returned data object whose lied value is true, and different again from svgHTML() receiving an object with no svg property. Keeping those branches separate prevents a report-reading worksheet from collapsing three different observations into one “failed” label.

For a support or research record, store only the minimum needed to reproduce the interpretation: pinned source commit, the field name, whether the report shows a value or placeholder, and which branch is being discussed. Do not store full browser profiles, raw user identifiers, or a full CreepJS result when a field-level note is enough. A hash or short label is still data and should not be retained without a stated purpose.

If a field is absent, write “not present in this displayed output” first. Then list possible source-level paths as hypotheses, such as the !fp.svg display branch or the catch branch, and mark them as unconfirmed unless the corresponding execution evidence exists. The pinned source can show that these branches exist; it cannot tell which one occurred for a report that has not been traced to its runtime path.

Separate SVG, Canvas, and DOM geometry

Three nearby topics use geometry-like values but answer different questions. SVG getBBox() and SVGTextContentElement methods operate on SVG graphics or text content. Canvas measureText() returns a TextMetrics object for canvas text. CSSOM getClientRects() returns rectangles for an element or range's rendered fragments. Similar names do not make the data interchangeable.

Use the API boundary to decide what to inspect. For SVG text length, trace the exact element and call such as getComputedTextLength() in src/svg/index.ts. For Canvas, inspect the actual measureText() input and properties such as width or actual bounding boxes. For CSSOM geometry, keep the element or range fragments and their reduction distinct. The ClientRects article gives the latter problem its own model; it should not be used to explain svgrectSystemSum.

This separation also helps with product questions. A profile's font choices may influence rendering, but a field from one collector does not disclose which file supplied a glyph, whether fallback occurred, or whether two distinct lengths came from different fonts. Those require evidence from the relevant font-loading and rendering path, not just the CreepJS output.

A minimal interpretation record

The following template keeps the claim attached to the evidence without turning the record into a user profile:

collector: CreepJS src/svg/index.ts
source_revision: 10aa6724cd33a1015db1574211890518cd04f0cc
report_field: emojiSet | svgrectSystemSum | lied | other named field
source_operation: exact method or branch
display_state: value shown | blocked placeholder | absent | error recorded
claim: source-bounded description of what was observed
not_established: OS, device, person, cause, or external verdict
retention: reason and deletion date

This is a documentation template, not a report generator. It deliberately omits numeric output samples, browser profiles, and per-user emoji lists. If a comparison requires a value, record the minimum value needed for that question and label its origin; do not imply it came from a fresh run unless a real run record is attached.

When comparing two authorized records, align the source revision, field, display branch, and transformation first. A comparison of emojiSet strings is not equivalent to a comparison of all input emoji. A comparison of svgrectSystemSum is not a comparison of the original length distribution because the Set discarded multiplicity. A comparison of lied is a comparison of the collector predicate, not proof that one environment is genuine and another is not.

BotBrowser capability and limits

BotBrowser documents profile-backed font bundles, fallback behavior, and cross-platform rendering controls. Those capabilities can support an authorized compatibility comparison where the question is how an application behaves with a documented profile configuration. The public font documentation does not promise identical SVGTextContentElement measurements for every platform, provide an SVG collector result, or define what CreepJS means by svgrectSystemSum.

The limitation is material: profile configuration cannot convert this source-only analysis into a runtime result or a statement about a specific device. No claim is made that BotBrowser runs CreepJS, changes a given SVG result, suppresses a report, or guarantees cross-platform equality. For a real compatibility investigation, compare a named application fixture under documented configurations and retain only the evidence needed to answer that compatibility question.

FAQ

Does svgrectSystemSum identify an operating system?

No. The pinned source builds it from numeric pieces of distinct stringified text lengths and scales the total by 0.00001. It does not define an OS label or an invertible classifier.

Is emojiSet the full list of supported emoji?

No. The collector retains the first emoji for each distinct getComputedTextLength() string. Repeated values are removed by a Set, and the code does not record a support count or font-file mapping.

Does extentOfChar measure the complete emoji?

The source passes EMOJIS[0] as the method argument. This source review does not establish that the returned value represents the complete emoji, so that interpretation is not made here.

Does lied: false prove the measurements are native?

No. It only means the accumulated source predicate did not become true on the represented path. It is not a general attestation about SVG behavior or every API call.

What does a blocked placeholder mean?

The renderer shows placeholders when fp.svg is missing. That visible state does not say why the property is absent. The collector's catch branch is another distinct path and should not be inferred without runtime evidence.

Sources

Practical conclusion

Read the CreepJS SVG card as a sequence of source-defined operations: a bounding-box reduction, one text-length measurement per fixture element, first-representative selection by unique string, a lossy scaled aggregate, and a shift/restore predicate. Keep bBox, extentOfChar, subStringLength, computedTextLength, emojiSet, svgrectSystemSum, and lied attached to their own transformations. Treat a missing placeholder and a caught error as separate states. None of these fields alone establishes an operating system, device, person, font inventory, or external verdict.

This article is part of the guide: What Is a Browser Fingerprint?

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.