1. Introduction
Modern web applications often dynamically update content in response to user interactions without performing a full page navigation. These interaction-initiated effects—such as structural DOM modifications, contentful paints, and history state changes—have historically been difficult to measure and attribute to the correct user actions.
Consider a typical Single Page Application pattern: a user clicks a product link, which triggers a click event handler. This handler initiates a network fetch for product details. When the response arrives, a callback is executed that dynamically injects the new content into the DOM and uses the History or Navigation API to update the URL. While this appears to the user as a navigation, existing metrics like Largest Contentful Paint (LCP) ([LARGEST-CONTENTFUL-PAINT]) only measure the initial page load, and Interaction to Next Paint (INP) only measures the immediate visual feedback of the click itself, leaving the significant subsequent rendering and "soft" navigation uncaptured.
This specification leverages the [EVENT-TIMING] API to define interactions and the [ASYNC-CONTEXT] proposal to track causality across asynchronous task boundaries. It defines how browsers can identify and report these effects, including "Soft Navigations," by integrating with [PAINT-TIMING] and [LARGEST-CONTENTFUL-PAINT] to attribute rendering changes to the performance timeline.
Furthermore, this specification integrates with the [CONTAINER-TIMING] proposal. When a user interaction causes DOM modifications, those modified nodes are designated as new "container roots." Any subsequent contentful paints within those subtrees are attributed to their respective roots, which are then traced back to the original interaction. This allows for efficient and accurate measurement of rich, dynamic page updates that occur far after the initial event dispatch.
2. Interaction Infrastructure
2.1. Navigation ID
A navigation id is a unique identifier assigned to each navigation (both hard and soft) within a global object’s lifetime.
Each global object has a current navigation id, an unsigned , initially set to the same value that initializes the Document’s initial interactionId value and the initial navigationId for Navigation Timing.
2.2. Interaction Context Intro
This specification leverages the TC39 [ASYNC-CONTEXT] proposal to handle this propagation. Every new user interaction (as defined by Event Timing) or relevant navigation event creates a new InteractionContext. This context is stored in a hidden, internal-only AsyncContext (denoted as ).
The web platform’s integration with AsyncContext ensures that this variable is automatically attached to asynchronous continuations (e.g., setTimeout, fetch, ), allowing the browser to attribute later effects back to the original interaction.
In addition to script propagation, this specification defines how user interactions that modify the DOM establish container roots (via [CONTAINER-TIMING]). These roots allow subsequent rendering effects, like contentful paints, to be traced back to the initiating interaction context even when no asynchronous script is currently active.
2.3. The InteractionContext struct
-
id, initially unset - The interaction id associated with this context.
-
start time, initially unset - Represents the start time of the interaction.
-
navigation type, initially unset - Represents the type of navigation (e.g., "push", "replace").
-
first URL value, initially unset - Represents the value of the URL at the first modification.
-
first URL update timestamp, initially unset - Represents the time of the first URL modification during this interaction.
-
first scroll timestamp, initially unset - Represents the time of the first scroll event during this interaction.
-
first input timestamp, initially unset - Represents the time of the first input event during this interaction.
-
first contentful paint, initially null - The first
InteractionContentfulPaintentry for this interaction. -
largest contentful paint, initially null - The largest
InteractionContentfulPaintentry for this interaction so far. -
total painted area, initially 0 - The sum of the areas of all contentful paints attributed to this interaction.
-
last URL value, initially unset - Represents the value of the most recent URL modification during this interaction.
-
emitted flag, initially false - Indicates that a soft navigation entry was emitted by the interaction.
SoftNavigationEntry’s name.
2.4. Infrastructure Algorithms
-
Let context be the value of the internal
AsyncContext. Variable.[[ ActiveInteractionContext]] -
Return context.
-
If document’s interaction id to interaction context[interaction id] exists, return document’s interaction id to interaction context[interaction id].
-
Let interaction context be a new InteractionContext.
-
Set interaction context’s id to interaction id.
-
Set interaction context’s start time to event’s
startTime. -
Set document’s interaction id to interaction context[interaction id] to interaction context.
-
Return interaction context.
-
Let timestamp be the current high resolution time given document’s relevant global object.
-
Let is scroll be true if event’s type is "scroll", and false otherwise.
-
Let is input be true if event’s type is an event type that would trigger has dispatched input event (as defined in [EVENT-TIMING]), and false otherwise.
-
If is scroll is false and is input is false, return.
-
For each interaction context of document’s interaction id to interaction context’s values:
-
If is scroll is true and interaction context’s first scroll timestamp is unset:
-
Set interaction context’s first scroll timestamp to timestamp.
-
-
If is input is true and interaction context’s first input timestamp is unset:
-
Set interaction context’s first input timestamp to timestamp.
-
-
-
If interaction id is null, return null.
-
Let interaction context be the result of calling get or create context for interaction with document, interaction id, and event.
-
Set the value of the internal
AsyncContext. Variableto interaction context.[[ ActiveInteractionContext]] -
Return interaction context.
-
If interaction context is null, return.
-
Set the value of the internal
AsyncContext. Variableto null.[[ ActiveInteractionContext]]
3. Interaction Contentful Paints
3.1. The InteractionContentfulPaint interface
[Exposed =Window ]interface :InteractionContentfulPaint PerformanceEntry {readonly attribute DOMHighResTimeStamp ;renderTime readonly attribute DOMHighResTimeStamp ;loadTime readonly attribute unsigned long long ;size readonly attribute DOMString ;id readonly attribute DOMString ;url readonly attribute Element ?;element readonly attribute unsigned long long ;interactionId object (); };toJSON InteractionContentfulPaint includes PaintTimingMixin ;
Each InteractionContentfulPaint has an associated paint timing info.
renderTime attribute’s getter must return this’s associated paint timing info’s rendering update end time.
The loadTime attribute’s getter must return this’s associated paint timing info’s implementation-defined presentation time.
The size attribute’s getter must return the contentful paint’s size.
The id attribute’s getter must return the contentful paint’s ID.
The url attribute’s getter must return the contentful paint’s URL.
The element attribute’s getter must return the Element associated with the contentful paint, or null if the element has been removed from the document.
The interactionId attribute’s getter must return the interaction id of the interaction that triggered this paint.
3.2. Interaction Contentful Paint Algorithms
-
Let entry be a new
InteractionContentfulPaintobject in global’s realm. -
Set entry’s
entryTypeto be "interaction-contentful-paint". -
Set entry’s
elementto be element. -
Set entry’s
interactionIdto interaction context’s id. -
Set entry’s associated paint timing info to a new paint timing info.
-
Set entry’s
renderTime,loadTime,size,id, andurlto the corresponding values of the contentful paint candidate as defined in the report largest contentful paint algorithm in [LARGEST-CONTENTFUL-PAINT]. -
Set entry’s
startTimeto interaction context’s start time. -
Set entry’s
durationto the difference between entry’srenderTimeand entry’sstartTime. -
Return entry.
-
Let global be document’s relevant global object.
-
Let paint entry be the result of calling create an interaction contentful paint entry with global, interaction context, and element. Note: For elements that require resource loading (e.g., an image with a new
src), the [PAINT-TIMING] specification is responsible for ensuring the "is loaded" criteria are met before triggering the contentful paint detection that eventually calls this algorithm. -
If interaction context’s first scroll timestamp is set and paint entry’s
renderTimeis greater than interaction context’s first scroll timestamp, return. -
If interaction context’s first input timestamp is set and paint entry’s
renderTimeis greater than interaction context’s first input timestamp, return. -
Queue paint entry.
-
Add paint entry to global’s performance entry buffer.
-
If interaction context’s first contentful paint is null:
-
Set interaction context’s first contentful paint to paint entry.
-
-
Set interaction context’s largest contentful paint to paint entry.
-
Increment interaction context’s total painted area by paint entry’s size.
InteractionContentfulPaint entries are emitted to the performance timeline as they are detected, independently of whether the interaction eventually results in a soft navigation. This allows developers to monitor rendering updates for all interactions.
4. Soft Navigations
4.1. The SoftNavigationEntry interface
[Exposed =Window ]interface :SoftNavigationEntry PerformanceEntry {readonly attribute DOMString ;navigationType readonly attribute unsigned long long ;interactionId readonly attribute InteractionContentfulPaint ?; };largestInteractionContentfulPaint SoftNavigationEntry includes PaintTimingMixin ;
Each SoftNavigationEntry has an associated paint timing info.
navigationType attribute’s getter must return the navigation type of the interaction that triggered the soft navigation.
The interactionId attribute’s getter must return the interaction id of the interaction that triggered the soft navigation.
The largestInteractionContentfulPaint attribute’s getter must return an InteractionContentfulPaint entry representing the largest contentful paint that occurred as a result of the interaction, or null if no such paint has occurred.
The name attribute’s getter must return the first URL value of the interaction that triggered the soft navigation.
The startTime attribute’s getter must return the start time of the interaction that triggered the soft navigation.
The duration attribute’s getter must return the difference between this’s presentationTime and this’s startTime at the time of emission.
largestInteractionContentfulPaint is expected to be non-null, as a confirmed soft navigation requires at least one detected interaction contentful paint to trigger its emission.
However, future iterations could evaluate whether a soft navigation could be considered committed immediately upon URL modification, prior to the first paint. In such a model, this field might be null at the time of emission.
The primary design consideration for this timing is the attribution of other timeline entries (e.g., LayoutShift, PerformanceResourceTiming, LongAnimationFrameTiming, etc) that occur in the interval between the URL modification and the first paint. Many sites commit the new URL immediately upon initiating a fetch request, for example, even while the current page remains in the previous navigation state. To ensure consistent timeline slicing, this specification attributes all such entries to the previous navigation identifier until the first paint confirms the transition.
4.2. Soft Navigation Algorithms
A soft navigation is a same-document navigation that satisfies the following conditions:
-
A same-document URL change occurs while an InteractionContext is active.
-
A contentful paint occurs that is attributed to the same InteractionContext.
-
If interaction context’s emitted is true, return.
-
If document’s active soft navigation candidate is not interaction context, return.
-
If interaction context’s first URL value is unset, return.
-
If interaction context’s first contentful paint is null, return.
-
Let global be document’s relevant global object.
-
Let url be interaction context’s first URL value.
-
Let entry be the result of calling create a soft navigation entry with global, interaction context, url, and interaction context’s start time.
-
Call emit soft navigation entry with global, entry.
-
Set interaction context’s emitted to true.
DOMHighResTimeStamp start time, run the following steps:
-
Let entry be a new
SoftNavigationEntryobject in global’s realm. -
Set entry’s
nameto be url. -
Set entry’s
entryTypeto be "soft-navigation". -
Set entry’s
startTimeto be start time. -
Let first paint be interaction context’s first contentful paint.
-
If first paint is not null:
-
Set entry’s associated paint timing info to first paint’s associated paint timing info.
-
-
Set entry’s
interactionIdto interaction context’s id (if available). -
Set entry’s
largestInteractionContentfulPaintto interaction context’s largest contentful paint. -
Return entry.
navigationId is set further down, in queue a PerformanceEntry.
SoftNavigationEntry entry, run the following steps:
-
Set entry’s navigation id to entry’s
interactionId. -
Set global’s current navigation id to entry’s
interactionId. -
Queue entry.
-
Add entry to global’s performance entry buffer.
-
Let interaction context be the result of calling get current interaction context.
-
If interaction context is null, return.
-
Set interaction context’s last URL value to url.
-
If interaction context’s first URL update timestamp is unset:
-
Set interaction context’s first URL update timestamp to the current high resolution time given document’s relevant global object.
-
Set interaction context’s first URL value to url.
-
Set interaction context’s navigation type to navigation type.
-
-
Set document’s active soft navigation candidate to interaction context.
-
Call evaluate soft navigation emission with document and interaction context.
5. The PerformanceEntry extension
[Exposed =(Window ,Worker )]partial interface PerformanceEntry {readonly attribute unsigned long long ; };navigationId
PerformanceEntry has an associated navigation id, an unsigned long long , initially 0.
The navigationId attribute’s getter must return this’s navigation id.
6. Specification Integrations
6.1. HTML integration
6.1.1. Document
Each document has an interaction id to interaction context, a map, initially empty.
Each document has an active soft navigation candidate, an InteractionContext or null, initially null.
6.1.2. History
documentsEntryChanged is true and if documentIsNew is false),
call process same document commit with the Document, entry’s url, and "traverse".
6.1.3. Node
Each node has an associated interaction context, initially null.
6.1.4. Hard Navigation
When a new global object global is created (e.g., during a "hard" navigation), its current navigation id is initialized as specified in § 2.1 Navigation ID.
PerformanceNavigationTiming entry for the initial navigation, the user agent must set its navigationId to the global object’s current navigation id.
6.2. Event Timing integration
This specification extends the [EVENT-TIMING] definition of interactions to include additional event types that are relevant for modern web applications and Single Page Applications.
The following event types are considered to be part of an interaction (as defined in [EVENT-TIMING]):
-
navigate -
popstate -
hashchange
When these events are dispatched as a result of a user interaction, the user agent must assign them a unique interaction id obtained by running the steps to get the next interactionId for the document’s relevant global object. If an event is triggered by a previous interaction that already has an assigned interaction id, the user agent should reuse that same identifier.
click handler manually manipulating history—the subsequent popstate or navigate events are not considered independent user interactions.
While these programmatically triggered events might not have the isTrusted flag set, they are correctly attributed back to the original interaction via [ASYNC-CONTEXT], as the history and navigation APIs are treated as asynchronous continuations of the initiating task.
navigate, popstate, and hashchange are used as internal signals for tracking soft navigations and assigning interactionId. This specification does not require these event types to be exposed as PerformanceEventTiming entries to the performance timeline, leaving that determination to the [EVENT-TIMING] specification.
processingStart and processingEnd hooks, and to ensure interactionId assignment happens early enough for this integration. Today, interactionId assignment is typically deferred until the end of the event processing.
unsigned long long for interactionId to ensure a safe global counter, while [EVENT-TIMING] currently defines it as unsigned long . This mismatch is expected to be resolved in future versions of both specifications.
Window window:
-
Set window’s interaction count to window’s interaction count plus 1.
-
Return window’s initial interactionId value plus (window’s interaction count times window’s interactionId increment).
-
Call update interaction contexts for event with document and event.
-
Let interaction id be the event’s interaction id.
-
Call interaction event processing start with document, interaction id, and event.
-
Call interaction event timing processing end with interaction context.
6.3. Largest Contentful Paint (LCP) integration
This specification hooks into the [LARGEST-CONTENTFUL-PAINT] algorithm to attribute paints to interactions.
-
Let contextToNewLargestCandidate be a new map.
-
For each record of paintedImages:
-
Let element be record’s pending image record element.
-
Let interaction context be element’s associated interaction context.
-
If interaction context is null, continue.
-
Let size be the effective visual size of element.
-
If size is null, continue.
-
If interaction context’s largest contentful paint is not null and size is less than or equal to interaction context’s largest contentful paint’s
size, continue. -
If contextToNewLargestCandidate[interaction context] is unset or size is greater than contextToNewLargestCandidate[interaction context]'s
size:-
Set contextToNewLargestCandidate[interaction context] to element.
-
-
-
For each textNode of paintedTextNodes:
-
Let interaction context be textNode’s associated interaction context.
-
If interaction context is null, continue.
-
Let size be the effective visual size of textNode.
-
If size is null, continue.
-
If interaction context’s largest contentful paint is not null and size is less than or equal to interaction context’s largest contentful paint’s
size, continue. -
If contextToNewLargestCandidate[interaction context] is unset or size is greater than contextToNewLargestCandidate[interaction context]'s
size:-
Set contextToNewLargestCandidate[interaction context] to textNode.
-
-
-
For each interaction context → newLargestElement in contextToNewLargestCandidate:
-
Call emit interaction contentful paint entry with newLargestElement, document, and interaction context.
-
Call evaluate soft navigation emission with document and interaction context.
-
6.4. Container Timing integration
The [CONTAINER-TIMING] API provides a mechanism to group rendering effects by their common DOM ancestor. This specification integrates with that mechanism to attribute rendering changes back to user interactions.
-
The
orclass styleattribute of an element is modified. -
A resource attribute (such as
srcon animgorvideoelement) is modified.
Run the following steps:
-
Let interaction context be the result of calling get current interaction context.
-
If interaction context is not null:
-
Set the node’s associated interaction context to interaction context.
-
Designate the node as a container root (as defined in [CONTAINER-TIMING]).
-
For the node and each of its descendants, remove them from the Document’s previously reported paints (as defined in [PAINT-TIMING]).
-
appendChild, innerHTML updates, etc.). It is up to the user agent to map these to internal implementation hooks that track document structure modifications.
Furthermore, to avoid expensive main-thread work during large DOM modifications, user agents are encouraged to optimize the removal of descendants from previously reported paints. Instead of an immediate exhaustive traversal, the user agent can mark the modified root as "dirty" and lazily propagate this state during subsequent tree walks (e.g., during layout or paint). Subtrees that are known to be non-visible or skipped by existing mechanisms like content or CSS containment could also be skipped during this reset process.
6.5. Performance Timeline integration
In queue a PerformanceEntry, after step 1 (initializing the entry), add the following steps:
-
If newEntry’s navigation id is 0:
-
Set newEntry’s navigation id to global’s current navigation id.
-
7. Overlapping Interactions and Race Conditions
Web applications often process multiple user interactions in rapid succession. This specification handles such overlapping interactions through the following model:
-
Context Independence: Each interaction manages its own InteractionContext independently. Multiple contexts can be "in flight" simultaneously, each tracking its own URL modifications and contentful paints.
-
Active Candidate Singleton: While many interactions can be active, the Document recognizes only one active soft navigation candidate at any given time.
-
Preemption: An interaction context becomes the active soft navigation candidate the moment it triggers its first same-document URL modification. If a subsequent interaction modification occurs, it preempts the previous one and becomes the new candidate.
-
Emission Validation: A
SoftNavigationEntryis only emitted for the interaction context that is the active soft navigation candidate at the moment the emission criteria (URL change + contentful paint) are met. This ensures that soft navigation reporting remains consistent with the document’s current visual and navigation state. -
Persistent Attribution: Even if an interaction is preempted as a soft navigation candidate, it continues to attribute and report its own
InteractionContentfulPaintentries as long as it remains active. This allows for accurate measurement of concurrent rendering updates that are not themselves considered "navigations."