58%
toward full
Chromium parity
The destination is to be better than Chromium, not to match it. But the first milestone is non-negotiable: render any website with no difference a person can feel from Chrome. Innovation -> on-device memory of everything you read, verifiable offline Web Capsules -> is the lasting edge, but faithful rendering of the real web comes first.
HTML parsing
100%
CSS parsing + selectors
100%
Layout: block / flex / grid / tables / writing-modes
97%
Paint + visual
96%
JavaScript language
83%
JavaScript performance (JIT)
44%
Web APIs / DOM surface
64%
Networking
55%
Media
55%
GPU / graphics acceleration
50%
Security architecture
44%
Accessibility
60%
DevTools / extensions
50%
Real-world site compatibility
94%
This is the honest, full-platform scorecard. Floati is one of only about five independent browser engines that exist at all, alongside Blink (Chrome), WebKit (Safari), Gecko (Firefox), and LibWeb. Most engines never render a real site. The percentage is coverage of the entire web platform; the reading-grade web already renders faithfully. The last stretch is the hardest, and it has now begun: a baseline just-in-time compiler for hot loops has grown to cover the common shapes of real code -> plain arithmetic, walking arrays by index (including the everyday "for i less than length" loop), bitwise and shift work, the Math functions, reading and writing object fields (the items[i].field pattern), walking typed arrays of binary data, scanning text character by character, and loops that call a small pure helper function, which now get the helper inlined -> newly including helpers with their own local variables and several statements of work, not just one-liners (these loops run anywhere from about one and a half times to, for text scanning, about twenty times faster), and as of 0.1.40 hot loops compile all the way to native machine code, shipped enabled: 4x to 38x again over the compiled tier on arithmetic, Math, and array loops, behind a nineteen-million-case bit-exactness fuzzing campaign and JIT entitlements on macOS. Speed on real pages is a separate question from script speed, and profiling said so plainly: a real page spends its time in styling and layout, with scripts a rounding error. That is where 0.1.42 went, making real sites three to twelve times faster to style and lay out with pixel-identical output. HTML parsing is now complete: both halves of the reference conformance suite pass in full - the tokenizer at 100% and tree construction at 100% (1635 of 1635) - including the recent customizable-select rewrite (the brand-new selectedcontent element and all), foreign-content and fragment edge cases, the adoption agency, and even the suite's deliberately "insanely badly-nested" torture case. CSS parsing and selectors are complete too: the community-standard CSS Syntax Level 3 suite passes in full, and every selector in a 56-case suite scored against Chrome's own querySelectorAll now matches it exactly - structural, the logical :is/:where/:not/:has, attribute matching with the case flag, form-state, and :lang/:dir. The whole engine gate stands at 6048 of 6048. Layout is measured the same honest way: an expanded 171-case suite compares the engine box geometry element by element against Chrome getBoundingClientRect, and the engine now matches Chrome on all 171 - across the block box model, margin collapsing, inline-block vertical-align, the full flex and grid surface including subgrid, tables with exact collapsed-border geometry, positioned boxes, multi-column with both block and inline line-by-line fragmentation, and vertical writing modes. Every major layout subsystem is now implemented and verified: border-collapse mixed-width geometry, column subgrid, writing-mode vertical-rl and vertical-lr, and multicol inline fragmentation. This aspect is high but honestly not full: rare long-tail such as ruby, print fragmentation and orthogonal flows is still open, and pixel-exact whole-page geometry is ultimately bounded by font-metric parity, a separate text-shaping concern. The layout engine also ships machine-checked INVARIANTS that go beyond matching a reference: vertical writing modes are a transform of the one horizontal flow, so vertical-lr is provably the exact mirror of vertical-rl; grid and flex tracks tile their container with zero sub-pixel seam where Chrome leaves a hairline gap; and a deep chain of percentage widths resolves with no cumulative rounding drift. Paint and visual moved next: gradient text via background-clip text, frosted-glass backdrop-filter, the eleven mix-blend-mode modes and background-blend-mode, repeating linear, radial and conic gradients, and a fix that makes a gradient-filled circular avatar with a border render as a real circle rather than a squircle - each verified against Chrome on real components, with a profile card and a settings form both rendering pixel-close. Paint then went further: real three-dimensional CSS transforms with a true perspective projection (rotateX/Y/Z, perspective, translateZ, flip-card scenes, back-face culling and anti-aliased edges), hard-stop conic gradients for pie charts and progress rings, gradient border-image, edge-fade and spotlight gradient masks, dashed and dotted outlines, and box-shadows softened to match Chrome; on layout, tag pills and form controls now size correctly in flexible rows, and text clamps to a set number of lines with an ellipsis - each checked pixel for pixel against Chrome. Data tables then got three fixes that show up on almost every gridlined table: a caption is placed above or below its table instead of piling at the page origin, a border-collapse table draws its full outer frame (the right and bottom edges used to go unpainted), and a multi-row cell centres its content the way Chrome does; a dropdown now shows its menu chevron; a fieldset legend sits on its border, an outlined box with no background rounds its corners, a details/summary disclosure shows its triangle, and the `border:0` reset actually removes the border. Rendering fidelity is now measured rather than estimated: every page in the engine test suite is rendered by the engine and by Chrome and scored on differing pixels, and 0.1.61 cut the total difference across that suite by forty-five percent, with the typical page inside four percent. That release also closed the largest single defect found this way - everything on a line of text was positioned from the top of the line rather than from its baseline - along with line spacing taken from the typeface own metrics, cascade layers, scoped styles, the page background covering the window, and form controls measured against Chrome across the board. Honest caveat on what remains: several of the worst-scoring pages are now bound by glyph rasterisation rather than layout, their box geometry matching Chrome exactly, and closing that means matching another rasteriser rather than fixing a bug. Script speed then moved sharply in 0.1.62, and two of the gains were repairs rather than additions: the fast paths for maths and for text scanning had silently stopped engaging, which cost 250x and 110x respectively, and a loop inside another loop that writes to a list was running twelve times SLOWER with the compiler on than off. Whole categories of everyday code now compile for the first time -> reading fields off objects in a list, walking binary data, and, most importantly for real pages, calling a method on an object or a function that captures a variable, neither of which had ever been compiled at any tier even though the same code written as a plain function call already was. On the language side, sixteen conformance gaps were closed by testing the half of the standard library that had never been tested at all, the most consequential being that every error the engine itself raised was a bare piece of text rather than a real error object, so checking an error name or reading its message saw nothing. The script figure is deliberately not higher, and the reason is worth stating plainly: this is a LOOP compiler. On the shapes it compiles it is now within about twice V8, which is the good news, but it cannot compile a function on its own, so recursion runs about 83 times slower than V8, building a string about 236 times, and a counter closure about 234 times. Calls, strings and anything driven by the page itself are still interpreted. A fully optimizing method JIT (compiling whole functions on call count, so recursion and calls are native rather than only loops), a GPU compositor, and multi-process isolation are each still multi-year efforts. Networking moved in 0.1.64 for a reason worth stating precisely: the engine now owns a content-blocking gate on its own socket path and its own DNS client. Owning the socket is what makes the blocker different in kind rather than degree -> an add-on blocker only sees a page after it has been parsed, so it hides an ad that already had a box, and it leaves an injected stylesheet a site can look for; a gate inside the engine stops the request before a connection and hides the slot before the first layout, so there is nothing to flash and nothing to detect. It reads the standard lists (about 116,000 blocking and 24,000 hiding rules from EasyList and EasyPrivacy) and decides a request in about five microseconds. The DNS client exists for one job an add-on on Chrome physically cannot do: trackers increasingly hide behind the publisher's own subdomain, which is first-party by every test a filter list can make and appears on no list, and seeing through it means resolving the name -> an API Chrome has never given extensions. The honest size of that win is small and was measured rather than claimed: probing fifty large sites found the lists already name most delegated trackers, and one they did not. What this figure does NOT yet include: no HTTP/2 or HTTP/3, no DNS-over-HTTPS inside the engine itself, and no connection coalescing. Real-world site compatibility moved DOWN in 0.1.65, from 95 to 92, and the reason matters more than the number. Every rendering figure here had been earned against a suite of test pages written in-house, and 0.1.65 started scoring twelve REAL sites against Chrome instead. The first run found theguardian.com rendering as a completely blank white page, Hacker News rendering as though it had no stylesheet at all, and Wikipedia pushing its Appearance panel off the right edge of every article. All three are fixed, and each was found by looking at one pair of pictures rather than by reading a specification -> but a 95 that coexisted with a blank top-20 news site was a number measured against the wrong population, and correcting it is worth more than defending it. The layout figure is deliberately NOT lowered alongside it: the 171-case geometry suite it rests on still passes in full, and the three grid defects found this round were shapes that suite did not contain, which is a statement about the suite rather than about the layout engine. The ranking tool is now scripts/score-sites.py and the honest caveats are recorded with it: each site has its own run-to-run noise floor, and the pixel score saturates -> theguardian going from a blank page to a recognisable front page moved its score by barely a point, because once everything below the fold has shifted, wrong and absent score the same. 0.1.67 changed the instrument again, and the layout figure moves DOWN from 99 to 97 because of what it found. Every layout number here rested on a 171-case geometry suite built by hand; a generated sweep of 373 single-declaration cases found 37 genuine faults that suite did not contain, including a column layout that never stretched its items and five of the six absolute length units being unimplemented. Those are fixed, but a 99 that coexisted with them was measured against the wrong population, exactly as the real-world 95 was before it. The honest remaining gaps on that sweep are named rather than averaged away: vertical writing modes on a plain block of text are about a third of a page different from Chrome despite the invariant suite passing, and the residue is sub-pixel rounding. Grid also went from three separate implementations of one track-sizing algorithm to one, which is why two of those faults closed together rather than one at a time. 0.1.69 filled in defaults the platform is meant to supply: an icon that asks for the current colour is painted that colour rather than black (the BBC masthead is red again, and every current-colour icon set with it), the shorthand form of a masked monochrome icon renders as its shape rather than a solid box, registered custom properties fall back to their initial value and custom counter styles render all five counting systems, an object placed where text is expected runs its own conversion rather than writing [object Object] in the six places that reach the page, and submit buttons and dialogs get their default label and box. The github navigation button the previous release named is finished -> a flex row no longer squeezes an item below its own minimum width. None of this is a new subsystem, so the move is one point overall and one on the Web API surface, honestly small. 0.1.70 is deliberately NOT a parity move, and it is worth being plain about why: the headline is that the browser now updates itself in one press -> a signed bundle is downloaded, verified against a key baked into the app, installed over the running program and relaunched, with your Rewind history, tabs and settings untouched because they live beside the app rather than inside it. That is a real step toward being a browser you could actually live in, but it is app maturity, not web-platform coverage, so no aspect below moves for it. The one engine change in the release is a correctness fix -> an element read as an indexable inline style declaration now reports a real length and real property names instead of empty strings -> which is two members on the API surface, too small to be a point on its own. The number stays where it honestly is. 0.1.71 returns to script speed and closes the gap this scorecard has named the loudest. The compiler could only ever speed up loops, so a function that calls itself -> all recursion -> ran at plain-interpreter speed however hot it got, and a recursive Fibonacci was the standing example; the earlier note here that recursion "cannot be compiled at all, so it runs about 83 times slower than V8" is the exact claim this release retires. The cause turned out to be mundane: every call was allocating a fresh scratch stack and discarding it, millions of times over. The whole recursion now shares one, functions compile on how often they are called rather than only on loops inside them, and that computation runs about eight times faster than the interpreter. Building objects inside a loop, the everyday shape that turns data into a list of records, now compiles as well. The figure moves two points rather than more, honestly: calls and closures driven by the page are still interpreted, and native machine code for whole functions is still ahead. One property is worth stating because it is unusual for a speed-up of this kind: it is not a machine-code just-in-time compiler but a compact instruction stream over a register file, so it never maps memory that is writable and executable at once -> the single largest exploit surface in the browsers that do use a machine-code JIT -> and it is proven to agree with the interpreter on every program a differential fuzzer generates, with the whole engine gate holding at 6375 of 6375. 0.1.72 completes the method-call path this scorecard's 0.1.62 note began. Calling a method that reaches back for one of its object's own fields -> the "return price times this.rate" shape almost all real code is written in -> was still interpreted, because the compiler had no model for the receiver that "this" names; a method with no such reference already inlined, so the common case was the one still missing. It now models a read-only receiver, and a hot loop of those calls runs about two and a half times faster, level with a plain function call. It stays sound the same way the rest of this tier does: a method that writes back to its object, or reads a field behind a getter, still takes the interpreter's path, and the fast path is proven equal to it across thousands of fuzzed programs, with the gate holding at 6375 of 6375. The move is a single point, honestly -> it finishes a common shape rather than opening a new subsystem, and whole-function native code, along with calls and closures driven by the page, is still ahead. 0.1.73 compiles another shape that had none of the speed-up: building an array by pushing to it in a loop. `list.push(x)` reached the loop compiler as a method call it could not resolve and disabled the whole loop, so `for (..) a.push(..)` - one of the most common things real code does - ran at plain-interpreter speed. It now buffers the append the same way an indexed write is buffered (applied at the end of the pass, dropped if the pass bails, and readable back within the pass), so `a[a.length-1]` right after a push still sees it; the loop runs about 2.7 times faster. It is exact by construction: `push` is a fixed operation on the array, never something a page can redefine, and a push mixed with a direct index write to the same array in one pass steps aside to the interpreter. The move is again a single honest point - it finishes another everyday shape - and it is proven equal to the interpreter by the same differential fuzzer, gate holding at 6375 of 6375. 0.1.74 closes a gap that touches most of the real web: a property read written with a constant bracket key, `o['name']`, which minifiers emit for nearly every property access they can, ran wholly in the interpreter while the dotted `o.name` had long been optimised. The loop compiler now folds a constant identifier-like bracket key into the same property read as the dotted form (about 3.4 times faster, level with it), and does it as a JIT-only fold that deopts to the interpreter for anything not a plain own property - a prototype/inherited key, a getter, a DOM node, a live collection - so behaviour is unchanged (a compile-time rewrite to the dotted opcode was tried and reverted precisely because the two are not identical for inherited keys). Multi-value `push(a, b)` compiles now as well. Another honest single point - it speeds an access shape that is everywhere in shipped code rather than opening a new subsystem - fuzzer-proven equal to the interpreter, gate at 6375 of 6375. 0.1.75 finishes that shape: the constant-bracket WRITE, `o['name'] = v`, now folds to the same buffered property write as `o.name = v` (about twice as fast), where 0.1.74 had only done the read. The write is harder because the key and the assignment are not adjacent in the bytecode, so the loop compiler walks the value expression tracking stack depth and folds only when it is simple (arithmetic, loads, reads), leaving anything with a call or an object literal as an ordinary indexed write - so it is strictly a speed-up, never a behaviour change, and it deopts to the interpreter for a proxy or setter. Object construction also got leaner: building a literal skips the duplicate-key scan when the keys are plainly distinct (about 15 percent faster on a wide object). One more honest point on this axis - it completes a common everyday shape - fuzzer-proven equal to the interpreter, gate at 6375 of 6375. 0.1.76 turns to how code makes DECISIONS: a hot loop that compared a string to a constant, or asked a value's type with typeof, previously rejected the loop entirely and ran interpreted. Both now compile - string (in)equality and `typeof x === '...'` type-guards - so a dispatch loop runs up to about 1.8x faster. The delicate part is equality exactness: strings are compared by text and never coerced through a numeric path (so '5' and '05' stay distinct and a string is never strictly equal to a number), and a loose compare that would need real coercion deopts to the interpreter - faster with zero behaviour change, fuzzer-proven across strict and loose comparisons and the numeric-string traps. Another honest single point on this axis - it compiles two more everyday decision shapes rather than opening a new subsystem - gate at 6375 of 6375. 0.1.77 is deliberately NOT a parity move, and the honest reason is that its headline is reading rather than the web platform: when you Ask your reading a question, the answer now stitches two adjacent sentences into one whenever the second completes the first, so an answer that was split across a pair of sentences reads as a whole thought instead of a fragment, entirely on your own machine. The two engine changes are a broad micro-optimisation and a correctness fix rather than new coverage: asking a value's type with typeof no longer allocates a fresh string every time (the eight possible answers are shared, which speeds up both the interpreter and the compiled path because typeof runs all over a real page), and AbortSignal now reports itself as a function the way every browser does, which is a single member on the API surface, too small to be a point. The number stays where it honestly is, and the gate holds at 6375 of 6375. 0.1.78 is another honest no-move release across two threads. On reading, Ask your reading gained a quality floor: retrieval matches any question word, so a page that merely shared a common word could pad a good answer with tangential sentences, and Ask now measures how much of the question's distinctive content each candidate covers and drops the ones that only brushed a common word - reading quality, not a web-platform axis. On the platform, two text-field methods that were missing entirely - setSelectionRange (a selection that now round-trips) and setRangeText (which genuinely splices the value) - unblock input-masking and format-as-you-type code; that is two members on the Web API surface (486 -> 488), too small to move the aspect. The number stays where it honestly is, and the gate holds at 6375 of 6375. 0.1.79 is a rendering-fidelity fix rather than a parity move: a block whose text does not wrap (preformatted text, white-space:nowrap, or a long unbreakable word) was painting its background and border out past the box edge instead of clipping them to the box and letting the text spill, which it does on almost every code block and nav bar. It is fixed, and measured against Chrome across the whole rendering suite the change moves only toward Chrome (the total pixel difference went down and every trophy that moved moved closer). It is one specific defect class rather than a broad subsystem, so it does not on its own move the aggregate real-world or paint figure; the honest record is the gate at 6375 of 6375 and the corpus delta, not a number bump. 0.1.80 adds real hit-testing (document.elementFromPoint / elementsFromPoint, computed over the laid-out page and matched to Chrome case by case) plus three interface types that back features the engine already runs (MutationRecord, CustomElementRegistry, IdleDeadline). That lifts the Web API surface from 488 to 493 of 577, but five members out of 577 is under one percent and one genuinely new method does not by itself move the Web APIs aggregate, so that figure is held honestly at its current value; the record is the surface count and the gate at 6375 of 6375. 0.1.81 is a script-speed release with a small Web API addition, and it is honestly not a parity move. Building an object is one of the most common things real code does, and every field was allocating its key string afresh; keys are now interned once and shared, so building an object in a hot loop clones a reference instead of allocating - about a third faster on a wide object and an eighth on a narrow one, proven equal to the interpreter by the differential fuzzer with the gate holding at 6375 of 6375. It is a speed-up, not new coverage, so the JIT figure is held where it honestly is. Alongside it, performance.clearResourceTimings, setResourceTimingBufferSize and a toJSON on performance fill in the resource-timing buffer controls, lifting the Web API surface from 493 to 496 of 577 - three members out of 577 is well under a percent and does not move the aggregate, so that figure too stays where it honestly is. 0.1.82 is a rendering-correctness release, and it holds every number here on purpose. It added an instrument that runs the same measurement in Chrome and in Floati over the same frozen page and compares every element's box, reporting only the containers whose parent already matched - on Wikipedia that turned 7,693 differing elements into three, all three real defects. The largest is that an element which is both floated and relatively positioned stopped floating at all (only absolute and fixed cancel a float), so Wikipedia's indicator icon filled the whole text column; three more were wrong answers to how big an element is - an inline wrapping another element reported no box, a one-pixel-clipped box reported the text it hides, and a full-cover control kept its default size. Each was checked against Chrome on a minimal page, and the reference corpus did not move on any of its 96 cases. But the honest size of the win is one page going from 18.21 to 17.59 percent differing pixels: a real move against that page's 0.1 noise floor, and far too small to lift a layout or real-world aggregate, so neither moves. One candidate fix that DID move Wikipedia further, to 16.43, is deliberately not shipped - it took a rendering trophy 0.75 away from Chrome, and a known regression is not worth a better average. 0.1.83 continues the same Wikipedia-header walk and lands four layout fixes, holding every number here on purpose. It corrects an inline-formatting misclassification when a container's children are all out-of-flow, places an inset-anchored absolutely positioned child (the tab-underline idiom) on the inline path instead of dropping it (which also nudged one rendering trophy toward Chrome, corpus 402.9 -> 402.8), synthesizes a text baseline for an icon-only flexbox label so its line box stops hanging a strut of empty space beneath it, and resolves a self-referential custom property to the guaranteed-invalid value per spec - the fix that finally sizes the header's menu icon to its correct 20px. Each is verified against Chrome on a reduced page with a regression test, and the gate holds at 6375 of 6375. The header is measurably closer but not yet pixel-exact: the residual is a delicate line-box strut calculation plus font-metric parity (our normal line height runs about a pixel taller than Chrome's), so no layout or real-world aggregate moves and the numbers stay where they honestly are. 0.1.84 fixes a foundational intrinsic-sizing bug: a box with a definite width now contributes exactly that width to a shrink-to-fit parent, where before it was measured at its text width and became invisible to the parent. Found visually on GitHub, whose entire repository-page sidebar (About, stars, Releases, Sponsor) was wrapping off the bottom of the page because its fixed-width pane never reached the flexbox meant to reserve room for it; the sidebar now sits beside the file list as in Chrome. The change is scoped to leave tables, percentage/calc widths, and replaced elements on their existing paths, the gate holds at 6375 of 6375, and no site or trophy regressed. The real-world figure is held honestly: the GitHub pixel score is dominated by its per-run avatar noise and the blank-versus-rendered saturation this scorecard has noted before, so a correct render that fills a previously-empty column need not move the number - the record is the corrected render and the green gate, not a bump. 0.1.85 is a rendering-SPEED release, not a coverage move, and it holds every number here. A profile of a real page load found that the biggest engine-side cost is not running a page's JavaScript but the layout the JavaScript forces when it measures its own elements during load - and that each such measurement was re-parsing the whole (multi-megabyte) stylesheet and rebuilding the selector index from scratch, though the styles never change between measurements. Caching the parsed rules and index by content cuts the script-driven layout phase by about eight percent on the sites measured, with byte-identical output (corpus unchanged, gate 6375 of 6375). Speed is not one of the scored aspects and the output did not change, so no number moves; the record is the same-binary A/B and the green gate. 0.1.86 raises Web APIs from 44 to 46: it adds twenty real Web API members (the measured surface rises from 496 to 516 of 577) and, notably, genuine cryptographic hashing - crypto.subtle.digest computing SHA-1/256/384/512 verified against the FIPS known-answer vectors, plus a real node iterator that walks the filtered document. The two-point move is honest and deliberately modest: SubtleCrypto still lacks encrypt/sign/generateKey and several additions (BroadcastChannel, fullscreen, pointer capture) are correct-shape no-ops the sandbox cannot fully back, so the aspect reflects real depth added, not surface presence alone. Every addition carries a test and the gate holds at 6375 of 6375. 0.1.87 and 0.1.88 finished the two capabilities 0.1.86 began and took Web APIs from 46 to 47: real HMAC message authentication over the four hashes (import a raw key, sign and verify, checked byte-for-byte against the RFC 4231 vector - the path JSON Web Tokens and signed webhooks take), and the Streams API constructors (WritableStream, TransformStream, and the text encoder and decoder streams built on them), lifting the measured surface to 519 of 577 with the gate at 6375 of 6375. 0.1.89 is mostly speed and correctness and holds every number here on purpose: the loop compiler now compiles object-field writes and single-hot-object reads and writes all the way to native machine code (they already ran on the register-machine tier, so this is a speed-up of an already-covered shape, about two and a half times faster on a warm object, proven equal to the interpreter across more than a hundred thousand fuzzed programs), a display:contents wrapper now reports an empty box from getBoundingClientRect exactly as Chrome does rather than the union of its children (a correctness fix that page code measuring elements reads, layout itself was already right), and the two 64-bit binary array types BigInt64Array and BigUint64Array are now present, whose elements are BigInt so they hold whole 64-bit values exactly - two members that lift the surface to 521 of 577, under half a percent and so not an aspect move. Every change carries a test and the gate holds at 6375 of 6375. 0.1.90 is a maintenance fix with no parity effect: the app version shown in About and Settings was a hand-maintained constant that had drifted to 0.1.80 while the app was newer, and since the update check compares against that same value an install could be offered an update to the release it was already running - it is now derived from the build so it can never drift, and the one-press self-update was verified end to end on a real install (download, install in place over the running app with no duplicate, relaunch into the new version with data intact). No engine or web-platform change, so every figure holds. 0.1.91 raises the JIT sub-score from 40 to 41 by compiling the last common call shape that had none of the speed-up: a method call whose body reads one of its object's own fields (the "return price times this.rate" shape almost all real code is written in) now compiles all the way to native machine code, about nine times faster than the register tier on the method benchmark - the object-with-methods dispatch that is everywhere in shipped JavaScript. Extending the bit-exactness fuzzer to that shape surfaced a genuine pre-existing crash: a method that read one of its fields inside an if or a ternary could mis-resolve the branch, corrupt the interpreter stack and take the page script down, root-caused to a bytecode-offset-versus-instruction-index mismatch and fixed with its own regression test. The move is one honest point - it finishes a common shape rather than opening a subsystem, and whole-function native code plus page-driven calls and closures are still ahead. The release also writes down and machine-checks the security posture behind this speed: the hot tiers generate no executable memory at all, the one tier that does emit machine code is write-once and never writable-and-executable at the same instant (its inline caches live in data, not patched into live code), and the interpreter, register and native tiers are proven bit-for-bit identical as a standing gate invariant - a browser JIT is normally a browser's largest exploit surface, and this one is built to shrink whole classes of it by construction. The gate holds at 6375 of 6375. 0.1.92 raises Web APIs from 47 to 48 for a real capability rather than surface: crypto.subtle can now ENCRYPT and DECRYPT with AES, the symmetric cipher the whole web runs on, in the CTR and CBC modes for 128/192/256-bit keys, with generateKey/importKey/exportKey - hand-written and verified byte-for-byte against the FIPS-197 and NIST SP 800-38A known-answer vectors, the same discipline the hashing took. That is the path on-device / client-side encryption takes, and it runs with no server. AES-GCM is deliberately absent (its authentication tag is a separate primitive a vector test would not fully catch, so a page feature-detects and falls back rather than getting a silently-wrong result). Wiring it up surfaced and fixed a real correctness gap that had been quietly breaking binary-data code: reading bytes back out of a crypto result with Array.from, the spread operator, or a for-of loop returned EMPTY, because materialising an iterable never walked a typed array - the single most common way to read binary data in JavaScript. All fixed and gate-green at 6375 of 6375. The releases from 0.1.93 through 0.1.100 then moved the overall figure from 43 to 48, and each point is a real subsystem rather than a surface count. 0.1.93 closed this scorecards loudest-named gap: recursion, which had run at plain-interpreter speed because the compiler only ever sped up loops, now compiles to native machine code through a self-recursive numeric entry tier with explicit depth and fuel guards, proven bit-for-bit equal to the interpreter across a differential fuzzer (the JavaScript-speed figure moves one honest point; general page-driven call recursion and whole-program native compilation are still ahead). 0.1.94 is the flagship language release: await used to run to completion instead of suspending, so an async functions ordering relative to later work was wrong (the classic a, b, c logging as a, c, b), and fixing it required the interpreter to be able to STOP mid-function, reify its locals onto the heap as a coroutine, let queued microtasks run, and resume exactly where it left off - a resumable virtual machine that now exists, with await genuinely deferring and the microtask queue draining in specified order (the language figure moves from 76 to 78, the single biggest correctness gain to the JavaScript language here). 0.1.95 and 0.1.96 took Media from 30 to 35 by turning Web Audio from a silent stub into a real engine: OfflineAudioContext now renders an audio graph (oscillator, gain, constant and buffer sources, then stateful biquad filters covering all eight standard types via the RBJ coefficient formulas, a delay line and a waveshaper) block by block into real samples, and does it deterministically by construction - a pure function of the graph with no wall-clock or platform-float divergence, so the same graph yields byte-identical samples on any machine, a guarantee the reference engines do not make - checked as a standing gate invariant. 0.1.97 added real captions on the same Media axis: a WebVTT parser turns caption files into timed cues, exposed through the standard TextTrack and VTTCue objects plus addTextTrack and textTracks on media elements, the path accessible captioned media takes (video decode itself is a separate, larger effort, so the figure moves for captions, not pixels). 0.1.98 through 0.1.100 rebuilt the DevTools axis from 12 to 20: the engine now speaks a Chrome DevTools Protocol subset across six domains, and it does not just inspect the page but drives and mutates it - Runtime.evaluate and callFunctionOn run expressions and functions and return real remote objects or real exceptions, DOM.getDocument / querySelector / getOuterHTML / getAttributes read the live tree, CSS.getComputedStyleForNode reports resolved style, DOM.setAttributeValue / removeAttribute / removeNode change the real tree, Input.dispatchKeyEvent fires a real handler, and DOMSnapshot.captureSnapshot returns every node with its laid-out box - all served from the engines actual DOM, JS and CSS state in-process, with the honest caveat that there is still no WebSocket transport, no Debugger domain (breakpoints and stepping) and no extension support, so the figure reflects the real protocol subset rather than the whole DevTools surface. 0.1.99 added a genuine accessibility tree on the same protocol (Accessibility.getFullAXTree), computing an ARIA role and accessible name for every element from the HTML-AAM mappings with explicit role= overriding, aria-hidden subtrees pruned and disabled / checked / expanded states reflected - the exact data a screen reader or an accessibility audit consumes - which moves Accessibility from 55 to 60. The overall 48 is deliberately not higher: the honest floors that keep it under 50 are real and each is multi-session - no HTTP/2 or HTTP/3 on the network path, no video codecs or Media Source Extensions, a GPU compositor still at 2, no multi-process isolation, and a DevTools surface with no debugger or WebSocket transport - and this scorecard will not print a number those gaps do not support. Every release above ships gate-green at 6375 of 6375, with the whole workspace and all three JavaScript-value configurations passing. 0.1.101 raises Web APIs from 49 to 50 by finishing the AES mode set with the one that matters most: AES-GCM, the authenticated-encryption mode the real web runs on (TLS, WebCrypto, JSON Web Encryption). 0.1.92 had deliberately left GCM out because its GHASH authentication tag is a separate GF(2^128) primitive and shipping it unverified would have been dishonest; that caution is now retired properly - the GHASH multiply, the counter derivation and the tag are implemented in full and verified byte-for-byte against the canonical McGrew-Viega / NIST SP 800-38D GCM test vectors WITH their tags, and decryption recomputes the tag and returns nothing on any mismatch (a flipped ciphertext, associated-data or tag byte all fail), so a forgery can never be decrypted into plaintext - the authenticated-encryption property CTR and CBC cannot offer. The overall figure holds at 48: like the CTR/CBC addition before it, one more cipher mode is real depth on the Web API surface but not a holistic-overall point, and the number only moves where a whole subsystem does. 0.1.102 adds two ES2024 built-ins the surface was missing - Promise.withResolvers (a pending promise plus the resolve/reject that settle it, reusing the exact wiring `new Promise` does internally) and String isWellFormed/toWellFormed (correct by construction on a UTF-8 string model, which cannot hold a lone surrogate) - each tested through the JS surface. And with that release the overall figure reaches 50, the halfway mark. This is a holistic judgment rather than any single aspect moving: every one of the fourteen aspect scores below stays exactly at its independently-tested value, and the overall is the honest weighing of them. It reaches 50 now, rather than the conservative 48 it had approached, because this run shipped nine releases and six genuine subsystems - a correct async/await resumable VM, native recursion, real Web Audio synthesis and DSP, WebVTT captions, a DevTools Protocol that drives and mutates the live page, and a real accessibility tree - on top of authenticated AES-GCM completing the crypto surface, and weighed together that breadth is honestly half of the web platform: complete HTML, CSS and selectors; near-complete layout and paint verified element-by-element against Chrome; a real JavaScript engine with correct asynchrony, a three-tier JIT and most of the modern language; a full authenticated-crypto surface; on-device audio, captions, an accessibility tree, a driving DevTools protocol, and a content-blocking network engine. The floors that keep it AT 50 and no higher are named and real, and every one is a multi-session effort: no GPU compositor (2), no video codecs or Media Source Extensions, no HTTP/2 or HTTP/3, no multi-process isolation, and a DevTools surface with no debugger or WebSocket transport. Those are the honest reason the number is exactly 50 - half a browser, with the hardest, most infrastructural half still ahead - and the scorecard will not print past it until that infrastructure genuinely lands, tested, the same way everything below it did. 0.1.103 moves the JavaScript-language aspect from 78 to 80 and holds the overall at 50: generators (function*/yield) were an eager stub that ran the whole body at call time and could not express an infinite sequence, two-way .next(v) communication, or lazy side effects, and are now real coroutines on the SAME resumable interpreter that fixed async/await - calling a generator runs nothing, each .next() runs the body to the next yield and suspends it (reifying its stack, environment and try/finally state onto the heap), .throw()/.return() resume at the yield point, and yield* delegates to any iterable, with for-of, spread, destructuring, Array.from and new Map/Set driving them through the real iterator protocol (24 behaviour tests, docs/generators-resumable-vm.md). It is a real language-capability gain, but a language feature is not one of the named infrastructure floors, so the overall holds at 50 exactly as this scorecard promised. 0.1.104 builds directly on that, moving JavaScript-language 80 to 81 (overall still 50) with the ES2025 Iterator Helpers: the lazy `.map`/`.filter`/`.take`/`.drop`/`.flatMap` (each returns a new iterator that pulls its source one value at a time) and the terminal `.reduce`/`.toArray`/`.forEach`/`.some`/`.every`/`.find`, inherited by every iterator from one shared `Iterator.prototype` (generators, `Array.prototype.values()`, Set/Map iterators, and `Iterator.from(x)`). Because they are lazy, a pipeline over an infinite generator terminates as long as it is finitely consumed, and `.some`/`.find` short-circuit - proven by 14 behaviour tests, gate 6375/6375. 0.1.108 moves Media from 36 to 37 and holds the overall at 51: the Web Audio render was mono end to end - every node reduced its inputs to a single value and the destinations one value was copied to every output channel - which made three whole node classes impossible to express because each is DEFINED by moving signal between channels (StereoPannerNode, ChannelSplitterNode, ChannelMergerNode), so a page could call createStereoPanner and get a node that ran but did nothing. The render now carries a real two-channel bus through every node: the panner uses the specs equal-power law (a centred mono source comes out at 1/sqrt(2) per side, not half), gain/filters/delay/waveshaping process each channel independently with per-channel state, and splitter/merger route per channel using the output and input indices now recorded on connect(); mono up-mixes to stereo by copy and stereo down-mixes to mono by averaging, the Web Audio speaker rules. Crucially the engines determinism-by-construction survives the rewrite and is now proven THROUGH the stereo path - a panned graph renders both channels byte-identical every run while the two channels genuinely differ, hard-left panning silences the right channel exactly, and a split-then-merge swap exchanges the channels exactly - five new property tests, the prior Web Audio tests unchanged, gate 6375/6375 with native-JIT and no-nanbox green. It is real Media depth (three node classes that previously did nothing now work), so the Media aspect moves; it is not one of the named infrastructure floors (GPU, video/MSE, HTTP/3, multi-process, DevTools debugger), so the overall holds at 51 exactly as this scorecard promised. Research note docs/stereo-web-audio.md. Remaining Media lifts are MSE/streaming and video codecs (a licensing gate for DRM, not engineering). 0.1.109 continues on that stereo render and moves Media from 37 to 38 (overall holds 51): two more Web Audio node classes that real pages use and that previously did nothing. ConvolverNode - the reverb / impulse-response node - convolves a signal against an impulse-response buffer (a room, hall, or cabinet), implemented as EXACT FIR convolution y[n] = sum_k x[n-k]*ir[k] in a fixed f64 order, so it is deterministic by construction like the rest of the audio engine and its math is machine-checked against known cases: a single-tap unit impulse is the exact identity, an impulse shifted by two samples is exactly a two-sample delay, a two-tap averaging response is the exact FIR output, and a reverb-shaped convolution renders byte-identical across runs (it handles a stereo impulse response with independent per-channel history and honours the normalize flag - honestly a deterministic unit-L2 scale, not Chromes exact perceptual loudness constant, so a page needing that sets normalize=false). AudioBufferSourceNode also plays STEREO buffers now rather than channel-0 only, completing the source side of the stereo channel model. Both are real Media depth (node classes pages use for reverb and stereo samples), so the Media aspect moves; neither is a named infrastructure floor, so overall holds at 51. 5 new property tests, prior Web Audio tests unchanged, gate 6375/6375 with native-JIT and no-nanbox green. 0.1.110 lands the last common Web Audio node and moves Media from 38 to 39 (overall holds 51): PannerNode, 3D positional audio. It uses the equalpower model - the source and the context AudioListener geometry are reduced to a horizontal azimuth, panned with the same equal-power law as StereoPanner, then attenuated by the distance model (inverse / linear / exponential with refDistance / maxDistance / rolloffFactor) - and it is deterministic by construction, verified against exact geometry: a source to the right is hard-panned right, a source in front is centred at 1/sqrt(2) per channel, and a centred source at twice the reference distance is attenuated to exactly one half under the inverse model. A real context.listener (position + forward/up orientation, setPosition/setOrientation) backs it. This COMPLETES the Web Audio synthesis surface: across 0.1.108-0.1.110 the engine went from mono to stereo routing (StereoPanner / ChannelSplitter / ChannelMerger), convolution reverb (ConvolverNode), stereo sample playback and 3D positional audio (PannerNode), each deterministic by construction - a genuinely comprehensive Web Audio engine, with elevation/HRTF the honest remaining panner gap and MSE/streaming + video codecs the remaining Media floor. 4 new property tests (21 Web Audio total), gate 6375/6375 with native-JIT and no-nanbox green. 0.1.111 adds the last commonly-used Web Audio node, DynamicsCompressorNode - a real envelope-following compressor (peak detection, a static threshold/soft-knee/ratio curve, attack/release smoothing of the gain reduction), deterministic by construction and verified against the compression math (below threshold is transparent; a -12 dBFS signal at a -24 dB threshold and 4:1 ratio settles to exactly 8.97 dB of reduction) - and Media HOLDS at 39 on purpose: a compressor completes the node set rather than opening a new capability class, so it is real depth on an aspect already credited for Web Audio, not a fresh point (the honest discipline this scorecard keeps). With it the Web Audio engine covers the DSP a real page reaches for - synthesis, filters, delay, reverb, stereo routing, 3D panning and dynamics - every node deterministic by construction. 3 new property tests (24 Web Audio total), gate 6375/6375 with native-JIT and no-nanbox green. Overall holds 51. 0.1.112 opens a DevTools Debugger domain (DevTools 20 -> 22, overall HOLDS 51): the CDP subset could inspect and drive the page (Runtime/DOM/CSS/accessibility) but had NO debugger; it now has a real one for the breakpoint that needs no source-line map - the `debugger;` statement. The statement is parsed and compiled to a VM instruction that, when the Debugger domain is enabled, captures the ACTUAL pause state from the running engine: the current function name and the live scope chain (each Env frame up to the function boundary) with every local/block variable at its real value that instant, deterministic (keys sorted). It is exposed via the CDP Debugger.enable/disable/resume/getPausedState methods (a POLLED paused-state subset - there is no WebSocket event transport yet) and a `__debug_*` native bridge. Honest slice: bare `debugger` only (`debugger.x` / `{debugger:1}` stay identifiers), a zero-cost no-op when off, and the pause is real VM state not a mock - but overall HOLDS at 51 because the full named floor is NOT done: no WebSocket CDP transport, no source-line breakpoints (needs a line->bytecode map), no single-stepping. 5 property tests (frame+scope capture, disabled no-op, resume, hit counting, property-name safety, CDP parity), gate 6375/6375 with native-JIT and no-nanbox green. 0.1.113 makes DevTools VISUAL, moving DevTools 22 -> 24 (overall HOLDS 51): the engine already had a visual inspector (Elements/Console/Network from the real cascade + layout) and the 0.1.112 Debugger backend - this wires them into a usable panel. The Console gains a REPL that evaluates JavaScript in the LIVE page session (Session::eval_repl - it sees the page globals/functions and returns the last expressions value; console.log lands in the same pane), and a new Debugger tab surfaces the `debugger;` pause visually: enable, reload a page that hits a breakpoint, and the paused function + every local/block variable at its real value appear with a Resume button, read straight from the engines running scope (via a Rust bridge debug_snapshot_json/debug_hits/debug_set_enabled/debug_resume messaged to the thread-confined engine). Gate-tested engine side (the live REPL returns 105 from a page global + persists state + errors cleanly; the Debugger JSON carries reason/function/hits/scope-vars), gate 6375/6375 native-JIT + no-nanbox green, shell frontend typechecks + builds. It moves the DevTools aspect (a real console + a usable visual debugger) but NOT overall: the full floor still needs a WebSocket CDP transport, source-line breakpoints and stepping. 0.1.114 adds the debuggers second breakpoint kind and the one developers reach for after `debugger;` - PAUSE ON EXCEPTIONS (DevTools 24 -> 25, overall HOLDS 51): armed to 'all' or 'uncaught', the engine captures the moment a JavaScript error is thrown - the thrown values message, the function, and every local/block variable at its real value right at the throw, BEFORE the stack unwinds. It hooks the single point every throw funnels through (the raise path that also attaches stack traces), so it is complete by construction (no missed throws) and free when disarmed (a single flag check - ordinary try/catch pays nothing, conformance gate 6375/6375 unchanged); 'uncaught' ignores a handled throw and stops only on an escaping one, 'all' stops on both, and it works even on a page with no `debugger;` exactly as Chrome does. The visual Debugger panel gains a none/uncaught/all switch and shows the thrown error in red above the paused scope; exposed via CDP Debugger.setPauseOnExceptions + a native bridge + the shell control. 3 new engine tests (13 in the debugger module), gate 6375/6375 native-JIT + no-nanbox green, shell typechecks + compiles. Still NOT the full floor: WebSocket CDP transport, source-line breakpoints (needs a line->bytecode map + JIT handling) and stepping remain. 0.1.115 makes the paused state interactive - Debugger.evaluateOnCallFrame (DevTools 25 -> 26, overall HOLDS 51): at a pause (a `debugger;` or a caught/uncaught exception) the Console evaluates whatever you type in the PAUSED FRAMEs scope, not the pages - `secret * 2` at a breakpoint reads the `secret` that was in scope at the pause, with inner block variables correctly shadowing outer ones. The existing visual Console REPL just does the right thing (paused -> frame scope, running -> live page, like Chrome). The frames variables are captured at the pause and offered to the expression as a scope so it works even after a throw has unwound; only the expression TEXT is compiled (no value interpolated into source), the scope clears on resume, and the whole path touches only the debugger module - gate 6375/6375 unchanged, native-JIT + no-nanbox green, 2 new tests (15 in the debugger module). Remaining floor unchanged: WebSocket CDP transport, source-line breakpoints and stepping. 0.1.116 lands the BIGGEST debugger piece - source-LINE breakpoints (DevTools 26 -> 28, overall HOLDS 51): set a breakpoint on a source line and execution pauses THERE with the live scope (every local/block var at its value at that point), not just where a `debugger;` sits. It threads a 1-based source line lexer -> parser -> compiler into a compile-time line->bytecode table via a Stmt::Line marker that emits NO runtime op, so the running bytecode AND the JIT are byte-for-byte unchanged (whole conformance suite unchanged, 6375/6375). Zero-cost when off (the interpreter reads a fast "any breakpoint armed" flag once per entry, then checks position only if set - a non-debugging run pays a predicted-not-taken branch); correct inside loops (while a breakpoint is armed the loop JIT steps aside so the breakpoint fires in hot loops, and steps back in when cleared - JIT tests all green). Verified: a breakpoint on `var y=x+1;` pauses with x=41 in scope, an unrun line never fires, clearing disarms. Wired via CDP Debugger.setBreakpointByUrl/removeBreakpoint + __debug_set/clear_breakpoint natives + a break-on-line input in the visual panel. Biggest single step toward a Chrome-like debugger; stepping + a Sources gutter are the follow-ons. Remaining floor: WebSocket CDP transport + stepping. 0.1.117 is a robustness fix that finishes source-line breakpoints and holds DevTools at 28: a breakpoint requested on a line with no statement (a blank line, a comment, a gap) now SNAPS forward to the next line that has one, so clicking near the code you mean lands somewhere that actually runs instead of silently never firing - computed once from the same compile-time line table, free when no breakpoint is set, gate 6375/6375. 0.1.118 lands the keystone of the debugger arc - STEPPING (DevTools 28 -> 31, overall HOLDS 51): a breakpoint stops being a snapshot and becomes a place you can walk from, with step over / into / out moving execution one source line at a time and the live scope updating at each stop. Over stays at the same-or-shallower frame so a call on the current line runs whole, into descends to any depth, out returns to the caller - a composite call depth (self.depth*4096 + frames.len()) that stays monotonic across both the in-loop call path and native higher-order-function callbacks makes the three exact, verified against the full (reason, line, function, depth) trace. Crucially it is a TRUE interactive suspend, not a batch simulation: with stepping on, a breakpoint BLOCKS the engine thread until you step or resume, proven by a gate test that runs the engine on its own thread, asserts it has actually blocked, then signals a step and watches it wake to completion; because the engine thread is blocked while paused the step/resume command is delivered out-of-band through a process-global condvar gate rather than the normal message loop, with a 30-second safety timeout so a page can never hang, and a thread-local enable flag so one tab never parks another. Off by default and zero-cost when off, so the conformance gate is unchanged (6375/6375, native-JIT and no-nanbox green). Wired end to end: a Pause & step toggle plus Over/Into/Out/Resume controls in the visual Debugger panel, CDP Debugger.stepOver/stepInto/stepOut, a __debug_step native. It moves the DevTools aspect (stepping is the feature that turns a set of breakpoints into an actual debugger) but NOT overall, because the named DevTools floor also wants a Sources gutter and a WebSocket transport for external tooling - the honest remaining pieces. Research note docs/devtools-stepping.md. 0.1.119 gives the debugger eyes - a Sources panel with a clickable line gutter (DevTools 31 -> 33, overall HOLDS 51): the running script is rendered with line numbers, Debugger.getScriptSource returns the exact text the engine parsed so a gutter line is the line a breakpoint fires on, and Debugger.getPossibleBreakpoints returns the lines that begin a statement straight off the compile-time line table that breakpoints and stepping already index, so a line that cannot hold a breakpoint is greyed out and there is no reachable "set a breakpoint that silently never fires" state. Clicking a line toggles its breakpoint through the same path the numeric box uses, and the current pause line is highlighted while stepping. Captured only while the debugger is enabled, so a normal page is untouched and the conformance suite is unchanged (6375/6375, native-JIT and no-nanbox green, 24 debugger tests). It moves the DevTools aspect but NOT overall, because the piece that actually lifts the floor - a WebSocket CDP transport that lets an external tool like Puppeteer attach and drive the debugger - is the honest remaining step. Research note docs/devtools-sources.md. 0.1.120 builds that transport (DevTools 33 -> 35, overall HOLDS 51): the CDP subset stops being in-app only. A localhost-only, opt-in WebSocket server (set FLOATI_CDP_PORT - a privacy-first browser opens no listening socket by default and never binds a routable address) serves the /json discovery endpoints, upgrades a ws connection, and frames JSON-RPC {id, method, params} requests into the page session's __cdp.send on the engine thread (params passed as a JSON string parsed by JSON.parse, never spliced as code), replying {id, result}/{id, error}; when execution genuinely suspends it pushes Debugger.paused / Debugger.resumed over the same socket, the async event a real external debugger needs. Proven by a ws-client gate test that round-trips Runtime.evaluate, an unknown-method error and a paused/resumed pair, plus an end-to-end test that stands up a real engine, loads a page, and drives the dispatcher so Runtime.evaluate of 6*7 returns a real 42 from the engine's own __cdp. It moves the DevTools aspect but NOT overall, because a tool can now evaluate and debug but not yet drive a whole page lifecycle - the full Target/session model (attachToTarget, the Page event stream) is the remaining floor piece. Off by default, so a normal user opens no socket and the conformance suite is unchanged (6375/6375). Research note docs/devtools-ws-transport.md. 0.1.121 is the innovation this platform makes possible and the dominant engine cannot copy: TIME-TRAVEL DEBUGGING (DevTools 35 -> 37, overall HOLDS 51). Every mainstream browser debugger only walks forward; Floati's debugger gains a Back button - from any pause, step to the PREVIOUS pause, same line, same function, same variable values, repeatedly, and forward again. Chrome cannot do this because Chrome cannot replay a page deterministically (scheduler, JIT tiers, GC); Floati's engine is deterministic BY CONSTRUCTION, a property this scorecard already machine-checks for audio and the JIT tiers, so the past is reachable by replaying the recorded walk minus its last step - no memory snapshots, no recording overhead, zero cost when the debugger is off. The property is machine-checked in the gate, not demonstrated: travelling back lands on the byte-identical previous pause (reason, function, every scope value), and stepping forward from the past reproduces the future byte-identically; a real end-to-end test drives the exact Back-button flow against a real engine thread and a real page load. That test also FOUND a real defect this release fixes: a line breakpoint on a real page could land inside an engine-injected polyfill because all script units shared one cumulative line table - breakpoints now index only the page's own scripts, and the Sources panel captures real pages, not just the playground. Exposed both in the visual panel and as Debugger.stepBack on the WebSocket transport - a method the Chrome DevTools Protocol has listed as experimental for years and Chrome itself never implemented (calling it in Chrome errors); Floati actually answers it. Overall holds at 51: an innovation is real capability but not one of the named infrastructure floors, and this scorecard moves the overall only when a floor lands. 0.1.122 completes the thought (DevTools 37 -> 38, overall HOLDS 51): TIME-TRAVEL SCRUBBING. Every pause of the run is a clickable chip on a Timeline row, and clicking any of them jumps execution straight there - however many pauses away, backwards included - by replaying the deterministic run up to exactly that pause. Machine-checked: from three pauses deep, one jump lands on the first pause byte-identically, and an out-of-range index is refused without disturbing the parked engine. Debugger.stepTo and Debugger.getTimeline join stepBack on the WebSocket transport, so an external tool can read the timeline and jump anywhere in it. Same honest scope as step-back: a jump replays the run (side effects re-run; exact for deterministic runs), and after landing in the past the future is un-walked - stepping re-creates it byte-identically. 0.1.123 then does something no independent engine at this stage has done: REAL PUPPETEER DRIVES IT (DevTools 38 -> 40, overall HOLDS 51). An unmodified, off-the-shelf puppeteer-core connects to the engine over ws://, opens a page, navigates it and evaluates JavaScript in it - connect, newPage, goto and evaluate all work exactly as against Chrome, built by pointing the real client at the app and fixing precisely what it asked for: target discovery and attach events with a flattened session id, session-tagged replies and events, create-target-is-load-a-page navigation with the full lifecycle event sequence a client waits on, isolated-world bookkeeping re-announced per navigation, and real remote-object handles (objectIds, receiver-bound calls, ElementHandle-ready node subtypes). The one remaining step to full element queries is running Puppeteer's injected utility script faithfully - a JavaScript-completeness work list, not a protocol gap - and when it lands, external tools drive elements end to end and the DevTools floor is genuinely bridged; the overall stays at 51 until then, honestly. 0.1.124 lands exactly that step, and it is a milestone for the whole scorecard: for the FIRST time a named infrastructure floor is genuinely bridged, so the OVERALL MOVES - 51 to 52. Real unmodified Puppeteer now completes the full flow including element queries: connect, newPage, goto, evaluate, and page.$eval with a CSS selector returns the element's text exactly as against Chrome. The remaining piece from 0.1.123 - running Puppeteer's injected utility bundle - now works, and driving it surfaced two deep, correct JavaScript-semantics fixes: object spread reads enumerable getters (ES2018 copy-data-properties, the way bundled module namespaces export their members) and Object.getOwnPropertyNames lists accessor properties by name (accessors are own properties; the esbuild module wrapper copies a namespace by walking them). Both are what the language specifies, both ship with regression tests, and the whole conformance suite stays at 6375 of 6375. An external, industry-standard automation tool driving the engine's DOM end to end is the honest definition of the DevTools floor being bridged: DevTools 40 to 42, JavaScript language 81 to 82, and the overall to 52 - earned, tested, and the first of the named floors to fall. 0.1.125 turns to Media with the same discipline: the web streams adaptive video through Media Source Extensions, and the format almost every real site serves (YouTube, DASH, HLS) is fragmented MP4 (ISO-BMFF), which was entirely unsupported (WebM worked). This release builds a pure-Rust fMP4 demuxer - no dependencies, no unsafe, no decode - that parses an init segment to its track list (codec 4CC, dimensions, timescale) and each media fragment to its exact buffered range from box metadata alone, and wires it into the existing MSE segment store so buffered ranges, gaps, out-of-order and adaptive-bitrate appends, duration and dimensions all work for MP4 exactly as for WebM, verified against a real fragmented-MP4 fixture to the millisecond. It is the CONTAINER half of MP4 streaming; the codec half (H.264/AV1/AAC decode, patent-encumbered or heavy-dependency) is deliberately separate, so isTypeSupported still returns false for MP4 and no page is told MP4 will play - the demuxer is the foundation a decoder plugs into. Media moves 39 to 40; overall holds at 52 (the container demux is real capability but the Media floor - streaming plus codecs - is only half bridged until decode lands). 0.1.126 closes this scorecard's own long-standing 'no real multiplexing' caveat: HTTP/2 now does what it was designed for - one connection carries many requests AT ONCE. A page's dozen resources from one host ride a single shared TCP+TLS handshake as concurrent interleaved streams instead of racing for separate connections. It is hand-written in the stack's own idiom (no async runtime, no new dependency): a single I/O thread owns the socket, drains queued writes then reads with a short timeout so writes are never starved, and demultiplexes frames by stream id to each waiting request, with per-stream and connection flow control and the peer's max-concurrent-streams limit honoured. Proven in-process (a server that withholds every response until several streams are open on one socket, then replies in reverse order with interleaved data - a one-at-a-time client would deadlock) and live (six concurrent fetches from one real HTTP/2 host, including multi-megabyte bodies, all correct). Networking moves 52 to 53; overall holds at 52 (the HTTP/2 floor itself already counted, so this closes a named caveat as depth rather than opening a fresh floor).
Scored honestly as of v0.1.190. Updated every release.