Skip to main content
Use the obfuscated decoration (§k, <obfuscated>, or JSON "obfuscated": true) for the native changing-glyph effect. The saved text and its formatting remain intact. Only the viewer’s displayed replacement ink changes.
This page describes the correction in library PR #110, after package 0.3.0. Version 0.3.0 uses an 80 ms interval. Check the changelog and your installed package version before relying on the corrected behavior.

Replacement behavior

The viewer chooses replacements on its owning window’s render frames, within the audited 60-FPS Vanilla menu budget. A 120/144-Hz browser does not speed up the effect. Slower rendering and missed frames do not trigger a burst of hidden catch-up replacements. Actual browser and Java-client frame rates can still differ; the upper bound does not guarantee exactly 60 changes each second or an identical random sequence. Browser frame timestamps have limited precision. The budget tolerates small rounding differences, bounded by half of the observed frame gap, rather than skipping otherwise valid 60-Hz frames. Candidates come from the selected profile’s font.advances. A candidate must be one Unicode code point. Width classes use the ceiling of the original non-bold advance; bold still contributes its separate layout advance. Spaces can occur in a replacement class. An original U+0020 space stays unchanged, while another space-like code point does not receive that special exemption. Changing a replacement does not change the stored text, glyph positions, line breaks, selection, source offsets, or undo history. The viewer retains its source accessibility label. The inline editor keeps its source text and editing state.

Pause motion

Set context.animations to false when you need a static view. Updating this setting does not edit the document. Re-enabling it resumes replacement without creating an undo transaction. The viewer follows live prefers-reduced-motion: reduce changes. Each instance owns its callback and preference listener; replacing or destroying a viewer cancels its previous work. A viewer mounted in an iframe uses that document’s window for scheduling and rendering resources. This control also affects other supported client-owned motion such as the menu panorama. It does not enable simulated MOTD frames, scrolling, blinking, or animated gradients.

Native evidence

The independent 2026-10-27 sessions used official Java 1.21.1 and 26.3, isolated Linux Xvfb displays, default resources, an 854×480 viewport, and GUI scale 2. The controlled status contained separate regular, bold, italic, and bold/italic runs, mixed widths, ordinary/non-breaking spaces, unsupported input code points, and a non-obfuscated white STATIC control. Each sequence spans approximately two seconds. Exact foreground-color masks change more often than the old 80 ms browser interval. Screen polling can miss rendered frames or sample near a buffer swap. These are detected image transitions, not timestamps instrumented inside the client renderer, and a random glyph can repeat. The versioned manifests record the complete supplied status, client/font/resource hashes, crop, monotonic before/after sample times, per-color mask hashes, and driver hash. They contain numerical evidence and checksums; raw crops and client resources stay local.

Source audits are separate evidence

Audits of the same official class files identify these paths:
  • 1.21.1: Font.StringRenderOutput.accept selects an obfuscated glyph while rendering. FontSet.getRandomGlyph chooses from a width group using the ceiling of the original non-bold advance.
  • 26.3: Font.getGlyph selects the replacement during text preparation, using the same non-bold ceiling class. The prepared glyph carries its styles and shadow rendering.
  • Both paths exclude an original code point 32 from replacement. Provider selection can include blank glyphs in candidate groups; missing groups use native missing-glyph behavior.
The 1.21.1 menu loop and 26.3 FramerateLimitTracker both cap out-of-level menus at 60 FPS. Modern inactivity/window throttling can reduce that further. Those audits explain the implementation path. They do not substitute for the actual sampled client output or certify the browser’s complete raster. The attempted 30-FPS settings control was rejected: the audited 1.21.1 menu loop returns a 60-FPS cap when no world is loaded, independently of its world FPS setting. It is not evidence of a 30-FPS obfuscation clock.

Reproduce a reference

Follow the native reference procedure with an authorized local client installation. Use an owned, dedicated Linux Xvfb :96, a separate game directory and default resources. Keep downloads, credentials, raw captures, and temporary execution notes outside Git.
1

Serve the exact status

Extract status from the selected manifest into a temporary JSON file and serve it with tools/reference/status-server.py on localhost port 25594. Add that address in the isolated client’s server list. Wait for the solid blue generated icon and verify all colored runs and the white STATIC control before capturing.
2

Capture a fresh sequence

From a source checkout with a JDK installed, use a new output directory outside the checkout. For 1.21.1, the MOTD crop starts at y=94; for 26.3, use y=96.
The helper refuses other displays and existing output directories. It samples 240 crops with monotonic timestamps, then encodes PNGs and hashes after sampling so image compression does not pace the capture.
3

Interpret the result

Compare consecutive masks hashes by color in frames.json. The white control should remain stable; animated colors should change. Record actual resource identities and the environment. A fresh random run need not have identical hashes or transition times.

Font and timing limits

The default client has more glyph providers than the independent bundled OFL font. Its entire random pool, glyph overhangs, unsupported-code-point behavior, and arbitrary resource-pack rasterization are not reproduced by that font. A separate read-only inspection of the active default font maps quantifies that difference: These are candidate code-point entries, not distinct glyph outlines. Both native width-4 classes contain U+0020 as a possible replacement. The pool audit metadata records counts and sorted-membership hashes. The debugger-enabled inspection is separate from the timing captures and reads only FontManager.fontSets["minecraft:default"].glyphsByWidth; the older mapped field names are recorded in the metadata. The cadence/class correction does not close that provider-coverage gap. Native calibration #106 retains the remaining pool/fallback scope; broader local resource-pack font support is tracked in #8. If a profile has no replacement class for an advance, the viewer retains its existing fallback rather than choosing an unrelated width. This is not a simulation of the native missing-glyph raster. The painter reuses a render canvas and a separate color parser per document. Its CSS/bitmap and parsed-color caches stay bounded. Sustained rainbow text does not allocate a new backing surface for every glyph on every frame. Browser regressions cover native frame budgets at 30/60/120/144/500-Hz browser refresh and rounded timestamps, normal/bold/italic paint, width classes, blank and supplementary-code-point candidates, stable geometry and editing state, live reduced motion, disposal, and iframe ownership. They do not certify an independent Windows Java client or shipping Safari. All bundled profiles remain verified: false. Continue with font/profile evidence and known limits.