Event Timing API

Editor’s Draft ,

More details about this document
This version:
https://w3c.github.io/event-timing/
Latest published version:
https://www.w3.org/TR/event-timing/
Test Suite:
https://github.com/web-platform-tests/wpt/tree/master/event-timing
Feedback:
GitHub
Editor:
( Google )
Former Editors:
( Google )
( Google )
Not Ready For Implementation

This spec is not yet ready for implementation. It exists in this repository to record the ideas and promote discussion.

Before attempting to implement this spec, please contact the editors.


Abstract

This document defines an API that provides web page authors with insights into the latency of certain events triggered by user interactions.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

GitHub Issues are preferred for discussion of this specification.

This document is governed by the 18 August 2025 W3C Process Document .

1. Introduction

1.1. Overview

This section is non-normative.

When a user engages with a website, they expect their actions to cause changes to the website quickly. In fact, research suggests that any user input that is not handled within 100ms is considered slow. Therefore, it is important to surface performance timing information about input events that could not achieve those guidelines.

A common way to monitor event latency consists of registering an event listener. The timestamp at which the event was created can be obtained via the event’s timeStamp . In addition, performance.now() could be called both at the beginning and at the end of the event handler logic. By subtracting the hardware timestamp from the timestamp obtained at the beginning of the event handler, the developer can compute the input delay : the time it takes for an input to start being processed. By subtracting the timestamp obtained at the beginning of the event handler from the timestamp obtained at the end of the event handler, the developer can compute the amount of synchronous work performed in the event handler. Finally, when inputs are handled synchronously, the duration from event hardware timestamp to the next paint after the event is handled is a useful user experience metric.

This approach has several fundamental flaws. First, requiring event listeners precludes measuring event latency very early in the page load because listeners might not be registered yet. Second, developers who are only interested in input delay might be forced to add new listeners to events that originally did not have them. This adds unnecessary performance overhead to the event latency calculation. And lastly, it would be very hard to measure asynchronous work caused by the event via this approach.

This specification provides an alternative to event latency monitoring that solves some of these problems. Since the user agent computes the timestamps, there is no need for event listeners in order to measure performance. This means that even events that occur very early in the page load can be captured. This also enables visibility into slow events without requiring analytics providers to attempt to patch and subscribe to every conceivable event. In addition to this, the website’s performance will not suffer from the overhead of unneeded event listeners. Finally, this specification allows developers to obtain detailed information about the timing of the rendering that occurs right after the event has been processed. This can be useful to measure the overhead of website modifications that are triggered by events.

1.2. Interactions

This section is non-normative.

A single user Interaction interaction (sometimes called a Gesture) is typically made up of multiple physical hardware input events. Each physical input event might cause the User Agent to dispatch several UIEvent s, and each of those might trigger multiple custom event listeners, or trigger distinct default actions.

For example, a single user "tap" interaction with a touchscreen device is actually made up of a sequence of physical input events:

Those physical input events might dispatch a series of UIEvent s:

These individual UIEvent s will each become candidates for their own PerformanceEventTiming entry reporting, which is useful for detailed timing.

Note: pointermove and touchmove are not currently considered for Event Timing .

However, this specification also defines a mechanism for grouping related PerformanceEventTiming s into Interaction interactions s via an interactionId . This mechanism can be used to define a page responsiveness metric called Interaction to Next Paint (INP) .

1.3. First Input

This section is non-normative.

The very first user Interaction interaction typically has a disproportionate impact on user experience, and is also often disproportionately slow.

To that effect, the Event Timing API exposes timing information about the first input of a Window , defined as the first PerformanceEventTiming entry with a non-0 interactionId .

Unlike most PerformanceEventTiming s, the first input entry is reported even if it does not exceed a provided durationThreshold , and is buffered even if it does not exceed the default duration threshold of 104ms. This mechanism can be used to define a page responsiveness metric called First Input Delay (FID) .

This also allows developers to better measure percentiles and performance improvements, by including data even from pages which are always very responsive, without having to register event handlers.

1.4. Events exposed

The Event Timing API exposes timing information only for certain events.

Given an event , to determine if it should be considered for Event Timing , run the following steps:
  1. If event ’s isTrusted attribute value is set to false, return false.

  2. If event ’s type is one of the following: auxclick , click , contextmenu , dblclick , mousedown , mouseenter , mouseleave , mouseout , mouseover , mouseup , pointerover , pointerenter , pointerdown , pointerup , pointercancel , pointerout , pointerleave , gotpointercapture , lostpointercapture , touchstart , touchend , touchcancel , keydown , keypress , keyup , beforeinput , input , compositionstart , compositionupdate , compositionend , dragstart , dragend , dragenter , dragleave , dragover , drop , return true.

  3. Return false.

Note: mousemove , pointermove , pointerrawupdate , touchmove , wheel , and drag are excluded because these are "continuous" events. The current API does not have enough guidance on how to count and aggregate these events to obtain meaningful performance metrics based on entries. Therefore, these event types are not exposed.

1.5. When events are measured

This section is non-normative. It explains at a high level the information that is exposed in the § 3 § 4 Processing model section.

Event timing information is only exposed for certain events, and only when the time difference between user input and paint operations that follow input processing exceeds a certain duration threshold.

The Event Timing API exposes a duration value, which is meant to be the time from when the physical user input occurs (estimated via the Event ’s timeStamp ) to the next time the rendering of the Event ’s relevant global object ’s associated Document is updated. This value is provided with 8 millisecond granularity.

By default, the Event Timing API buffers and exposes entries when the duration is 104 or greater, but a developer can set up a PerformanceObserver to observe future entries with a different threshold. Note that this does not change the entries that are buffered and hence the buffered flag only enables receiving past entries with duration greater than or equal to the default threshold.

An Event ’s delay is the difference between the time when the browser is about to run event handlers for the event and the Event ’s timeStamp . The former point in time is exposed as the PerformanceEventTiming ’s processingStart , whereas the latter is exposed as PerformanceEventTiming ’s startTime . Therefore, an Event ’s delay can be computed as processingStart - startTime .

Note that the Event Timing API creates entries for events regardless of whether they have any event listeners. In particular, the first click or the first key might not be the user actually trying to interact with the page functionality; many users do things like select text while they’re reading or click in blank areas to control what has focus. This is a design choice to capture problems with pages which register their event listeners too late and to capture performance of inputs that are meaningful despite not having event listeners, such as hover effects. Developers can choose to ignore such entries by ignoring those with essentially zero values of processingEnd - processingStart , as processingEnd is the time when the event dispatch algorithm algorithm has concluded. .

1.6. Usage example

const observer = new PerformanceObserver(function(list, obs) {
    for (let entry of list.getEntries()) {
        // Input Delay
        const inputDelay = entry.processingStart - entry.startTime;
        // Processing duration
        const processingDuration = entry.processingEnd - entry.processingStart;
        // Presentation Delay (approximate)
        const presentationDelay = Math.max(0, entry.startTime + entry.duration - entry.processingEnd);
        // Obtain some information about the target of this event, such as the id.
        const targetId = entry.target ? entry.target.id : 'unknown-target';
        console.log(entry.entryType, entry.name, entry.duration, { inputDelay, processingDuration, presentationDelay });
    }
});
observer.observe({ type: 'first-input', buffered: true });
observer.observe({ type: 'event', buffered: true, durationThreshold: 40 });

The following example computes a dictionary mapping interactionId to the maximum duration of any of its events. This dictionary can later be aggregated and reported to analytics.

let maxDurations = {};
new PerformanceObserver(list => {
    for (let entry of list.getEntries()) {
        if (entry.interactionId > 0) {
            let id = entry.interactionId;
            if (!maxDurations[id]) {
                maxDurations[id] = entry.duration;
            } else {
                maxDurations[id] = Math.max(maxDurations[id], entry.duration);
            }
        }
    }
}).observe({ type: 'event', buffered: true, durationThreshold: 16 });

The following are sample use cases that could be achieved by using this API:

2. Event Timing

Event Timing adds the following interfaces:

2.1. PerformanceEventTiming interface

[Exposed=Window]
interface PerformanceEventTiming : PerformanceEntry {
    ;
    ;
    ;

    readonly attribute DOMHighResTimeStamp processingStart;
    readonly attribute DOMHighResTimeStamp processingEnd;
    readonly attribute boolean cancelable;
    readonly attribute Node? target;
    ;
    ;

    readonly attribute DOMString targetSelector;
    readonly attribute unsigned long long interactionId;
    [Default] object toJSON();
};
A PerformanceEventTiming object reports timing information about one associated Event .

Each PerformanceEventTiming object has these associated concepts, all of which are initially set to null : concepts:

The name attribute’s getter provides the must return this ’s associated event ’s type . entryType attribute value.

The entryType attribute’s getter returns " event " (for long events) or " first - input " (for the first user interaction). startTime must return this ’s entry type .

The startTime attribute’s getter returns the must return this ’s associated event ’s timeStamp . duration attribute value.

The duration attribute’s getter returns the difference between the next time the update the rendering steps are completed for the associated event must return this ’s Document render time after the associated event minus this has been dispatched, and the ’s startTime , rounded to the nearest 8ms. PerformanceEventTiming has the following additional attributes:

The processingStart The processingStart attribute’s getter returns a must return this ’s processing start timestamp captured at the beginning of the event dispatch algorithm . This is when event handlers are about to be executed.

The processingEnd The processingEnd attribute’s getter returns a timestamp captured at the must return this ’s processing end of the event dispatch algorithm timestamp . This is when event handlers have finished executing. It’s equal to processingStart when there are no such event handlers.

The cancelable The cancelable attribute’s getter returns the must return this ’s associated event ’s cancelable attribute value. target

The target attribute’s getter returns must perform the associated event following steps:

  1. If this ’s last target when such Node eventTarget is not disconnected nor in the shadow DOM. exposed for paint timing given null, return null.

  2. Return this ’s eventTarget .

The targetSelector The targetSelector attribute’s getter returns a string that identifies must return the associated event result of generate a CSS selector ’s last target given this . ’s eventTarget .

The interactionId The interactionId attribute’s getter returns a number that uniquely identifies the user Interaction which triggered the associated event . This attribute is 0 unless the associated event must return this ’s type interactionId attribute value if it is one of: A pointerdown , pointerup , not null, or click 0 otherwise.

, and belongs Note: A user agent implementing the Event Timing API would need to a tap or drag gesture. Note that pointerdown include " first - input that ends " and " event " in scroll is excluded. A keydown supportedEntryTypes or for keyup Window , belongs contexts. This allows developers to a user key press. detect support for event timing.

2.2. EventCounts interface

[Exposed=Window]
interface EventCounts {
    readonly maplike<DOMString, unsigned long long>;
};

The EventCounts object is a map where the keys are event types and the values are the number of events that have been dispatched that are of that type . Only events whose type is supported by PerformanceEventTiming entries (see section § 1.4 Events exposed ) are counted via this map.

2.3. Extensions to the Performance interface

[Exposed=Window]
partial interface Performance {
    [SameObject] readonly attribute EventCounts eventCounts;
    readonly attribute unsigned long long interactionCount;
};

The eventCounts attribute’s getter returns this ’s relevant global object ’s eventCounts event counts .

The interactionCount attribute’s getter returns this ’s relevant global object ’s interactionCount interaction count .

3. Processing model Modifications to other specifications

3.1. Modifications to the DOM specification

This section will be removed once [DOM] has been modified.

We modify the event dispatch algorithm given event as follows.

Right after step 1, we add the following steps:

  1. Let interactionId be the result of computing interactionId given event . Let timingEntry be the result of initializing and recording event timing processing start given event , and the current high resolution time , and interactionId . .

Right before the returning step of that algorithm, add the following step:

  1. Finalize Record event timing processing end passing given timingEntry , event , target , and the current high resolution time as inputs.

Note: If a user agent skips the event dispatch algorithm , it can still choose to include an entry for that Event . In this case, it will estimate the value of processingStart and set the processingEnd to the same value.

3.2. Modifications to the HTML specification

This section will be removed once [HTML] has been modified.

Each Window has the following associated concepts:
In the update the rendering step within the event loop processing model , add a step right after the step that calls mark paint timing :
  1. For each fully active Document doc that is either removed from docs (due to being non-renderable or unnecessary) or is fully active in docs , invoke (after calling mark paint timing ), run the algorithm to dispatch pending following steps:

    1. Record event timing render time for doc with the current high resolution time .

    2. Flush Event Timing entries for that Document . doc .

Note: To ensure timely reporting, event timing entries are updated and flushed even when a rendering update is skipped (e.g., if the document is non-renderable). In these cases, the current high resolution time serves as a "fallback" for the render time. Formally distinguishing between this fallback and a true "paint" time is left as a potential future extension.

3.3. Modifications to the Performance Timeline specification

This section will be removed once [PERFORMANCE-TIMELINE-2] had been modified.

The PerformanceObserverInit dictionary is augmented:

partial dictionary PerformanceObserverInit {
    DOMHighResTimeStamp durationThreshold;
};

3.4. 4. Should add PerformanceEventTiming Processing model

Note: 4.1. The following algorithm is used in the [PERFORMANCE-TIMELINE-2] specification to determine when a PerformanceEventTiming entry needs to be added to the buffer of a PerformanceObserver Initialize and record event timing processing start or to the performance timeline, as described in the registry . Given a PerformanceEventTiming

To initialize and record event timing processing start given entry event and processingStartTimestamp :
  1. If event should not be considered for Event Timing , return null.

  2. Let timingEntry be a new PerformanceObserverInit PerformanceEventTiming options , to determine if we should add PerformanceEventTiming , object with entry event and optionally ’s relevant realm .

  3. Set options timingEntry as inputs, run the following steps: ’s associated event to event .

  4. If Set entry timingEntry ’s entryType entry type attribute value equals to " first - input event ", return true. ".

  5. Assert that Set entry timingEntry ’s entryType processing start timestamp attribute value equals " to processingStartTimestamp .

  6. Set timingEntry ’s interactionId to the result of compute interactionId given event ". and timingEntry .

  7. Let minDuration window be computed as follows: event ’s relevant global object .

  8. If options window is does not present or if implement Window , return null.

  9. Let options type be event ’s durationThreshold type is not present, let .

  10. Let minDuration event counts be 104. window ’s event counts .

  11. Otherwise, let Assert that minDuration event counts be the maximum between 16 Contains type .

  12. Set event counts [ type ] to event counts [ type ] + 1.

  13. If window ’s has dispatched input event is false and options timingEntry ’s durationThreshold interactionId value. is not 0, run the following steps:

    1. If Set entry window ’s has dispatched input event to true.

    Note: has dispatched input event is set to true as soon as an interactive event is initialized. For duration pointerdown attribute value entries, interactionId is greater than or equal to minDuration , return true. still unknown at this point, but other specifications (such as Largest Contentful Paint ) which use this, will observe both pointer interactions and scroll as input, anyway.

  14. Otherwise, return false. Return timingEntry .

3.5. 4.2. Increasing interaction count Record event timing processing end

When asked to increase interaction count
To record event timing processing end given a Window window object, perform the following steps: timingEntry , target , and processingEndTimestamp :
  1. Increase If window timingEntry ’s user interaction value value by a small number chosen by the user agent. is null, then return.

  2. Let interactionCount relevantGlobal be window target ’s interactionCount relevant global object .

  3. Set interactionCount timingEntry ’s processing end timestamp to interactionCount processingEndTimestamp .

  4. Assert that target + 1. implements Node .

    Note: The user interaction value is increased by a small number chosen by the user agent instead of 1 This assertion holds due to discourage developers from considering it as a counter of the number types of user interactions that have occurred in the web application. This allows events supported by the user agent to choose to eagerly assign a user interaction value Event Timing API.

  5. Set timingEntry ’s eventTarget (i.e. at pointerdown) and then discard it (i.e. after pointercancel), rather than to lazily compute it. target .

  6. A user agent may choose Append timingEntry to increase it by a small random integer every time, or choose a constant. A user agent must not use a shared global user interaction value relevantGlobal ’s pending Event Timing entries .

4.3. Record event timing render time s for all

To record event timing render time given a Windows Document , because this could introduce cross-origin leaks. doc and a renderingTimestamp :
  1. Let window be doc ’s relevant global object .

  2. For each timingEntry in window ’s pending Event Timing entries :

    1. If timingEntry ’s render time is 0, set timingEntry ’s render time to renderingTimestamp .

3.6. 4.4. Computing Compute interactionId

When asked to
To compute interactionId with event as input, run the following steps: If given an Event event ’s and a isTrusted PerformanceEventTiming attribute value is false, return 0. entry :
  1. Let type be event ’s type attribute value.

  2. If type is not one among keyup keydown , compositionstart keypress , keyup , input , pointerdown , pointercancel , pointerup , click , or contextmenu , return 0.

    Note: keydown and pointerdown are marked pending in finalize event timing , and then updated later when computing interactionId for future events (like keyup and pointerup ).
  3. Let window be event ’s relevant global object .

  4. Let pendingKeyDowns be window ’s pending key downs . Let pointerMap be window ’s pointer interaction value map . Let pendingPointerDowns newInteractionId be window ’s pending pointer downs . null.

  5. If type event is a keyup PointerEvent : If and event ’s isComposing pointerId attribute value is true, return 0. greater than or equal to 0:

    1. Let code be Set event newInteractionId ’s keyCode to the result of compute pointer interactionId attribute value. If given pendingKeyDowns [ window , code ] does not exist, return 0. Let event , and entry be pendingKeyDowns [ code ]. Increase interaction count on window .

  6. Let interactionId be window ’s user interaction value value. Else:

    1. Set entry newInteractionId ’s interactionId to the result of computing the keyboard interactionId . Add entry to window ’s entries to be queued . Remove given pendingKeyDowns [ code window ]. Return and interactionId event .

    If

Note: At this point, newInteractionId can be null only if type is compositionstart pointerdown : . Keyboard interactions and other pointer interactions either compute a valid ID or fallback to 0 directly in their respective sub-algorithms.

  1. For each Return entry in newInteractionId .

To get the values next interactionId for a Window of pendingKeyDowns window :
  1. Append Set entry window ’s interaction count to window ’s entries to be queued . Clear interaction count pendingKeyDowns . plus 1.

  2. Return 0. If type window is input ’s initial interactionId value : If plus ( event window is not an instance of InputEvent ’s interaction count , return 0. Note: This check is done to exclude times window ’s interactionId increment ).

To compute pointer interactionId given a Events Window for which the window , an type Event is event , and a input PerformanceEventTiming but that are not about modified text content. entry :
  1. If Let type be event ’s isComposing type attribute value is false, return 0. value.

  2. Let pointerId be event ’s Increase interaction count pointerId on window . attribute value.

  3. Return Let activePointerInteractions be window ’s user active pointer interaction value map .

  4. Otherwise ( Let type pendingPointerDowns is pointercancel , pointerup , click , or contextmenu ): be window ’s pending pointerdown map .

  5. Let pointerId newInteractionId be event ’s pointerId attribute value. null.

  6. If type is click pointerdown :

    1. If pointerMap pendingPointerDowns [ pointerId ] does not exist, return 0. exists:

      1. Let Run resolve pending pointerdown given value window be and pointerMap pendingPointerDowns [ pointerId ].

    2. Remove Set pointerMap pendingPointerDowns [ pointerId ]. Return ] to value entry .

  7. Assert that Else if type is pointerup , pointercancel contextmenu , or contextmenu click . :

    1. If pendingPointerDowns [ pointerId ] does not exist: exists:

      1. If Set type newInteractionId is contextmenu to the result of resolve pending pointerdown , return given window ’s user interaction value . and pendingPointerDowns [ pointerId ].

    2. If type newInteractionId is pointerup null and window activePointerInteractions ’s is contextmenu triggered flag is true: [ pointerId ] exists:

      1. Set window newInteractionId ’s is contextmenu triggered flag to false. Return window ’s user interaction value . Otherwise, return 0. Let pointerDownEntry be pendingPointerDowns activePointerInteractions [ pointerId ].

    3. Assert that If pointerDownEntry newInteractionId is a null:

      1. Set newInteractionId to the result of getting the next interactionId given window .

      Note: Most pointer interactions are expected to have an associated PerformanceEventTiming pointerdown entry. event, which would have assigned an interactionId in the steps above. However, some special input devices (for example, accessibility-related software) can simulate certain trusted pointer events without following the usual event sequence.

  8. If Else if type is pointerup or contextmenu pointercancel :

    1. Increase interaction count on window . Set If pointerMap pendingPointerDowns [ pointerId ] to window ’s user interaction value . exists:

      1. Set pointerDownEntry ’s interactionId to pointerMap pendingPointerDowns [ pointerId ]. Append pointerDownEntry to window ’s entries ]'s interactionId to be queued . 0.

      2. Remove pendingPointerDowns [ pointerId ].

    2. If type is contextmenu , set Set window newInteractionId ’s is contextmenu triggered to true. If type is pointercancel , return 0.

  9. Return pointerMap [ pointerId ]. newInteractionId .

Note: The algorithm attempts to assign events to the corresponding interaction IDs. For keyboard events, a keydown triggers a new interaction ID, whereas
To resolve pending pointerdown given a keyup Window has to match its ID with a previous keydown . For pointer events, when we get window and a pointerdown we have to wait until pointercancel PerformanceEventTiming or pointerup pointerDownEntry :
  1. Let newInteractionId be the result of getting the next interactionId occur to know its given window .

  2. Set pointerDownEntry ’s interactionId . We try to match click with a previous interaction ID from a pointerdown . If pointercancel or pointerup happens, we’ll newInteractionId .

  3. Let pointerId be ready to set the interactionId pointerDownEntry ’s associated event for the stored entry corresponding to ’s pointerdown pointerId . If it is pointercancel , this means we do not want to assign a new

  4. Set window ’s active pointer interaction ID map [ pointerId ] to the newInteractionId .

  5. pointerdown Remove . If it is pointerup window ’s pending pointerdown map , we [ pointerId ].

  6. Return newInteractionId .

To compute a new interaction ID and set it on both the keyboard interactionId given a pointerdown Window window and the pointerup (and later, the an click Event if it occurs). 3.7. Initialize event timing When asked to initialize event timing , with event , processingStart , and interactionId as inputs, run the following steps: :
  1. If the algorithm to determine if Let event type should be considered for Event Timing event ’s type returns false, then return null. attribute value.

  2. Let timingEntry activeKeyInteractions be a new PerformanceEventTiming object with event window ’s relevant realm active key interaction map .

  3. Set Let timingEntry newInteractionId ’s name to be null.

  4. If event type ’s is type keydown attribute value. :

    1. Set timingEntry newInteractionId ’s entryType to " event ". the result of getting the next interactionId given window .

    2. Set timingEntry activeKeyInteractions ’s startTime to [ event ’s timeStamp keyCode attribute value. ] to newInteractionId .

    3. Set timingEntry window ’s processingStart last keydown interactionId to processingStart newInteractionId .

      Set timingEntry ’s

    Note: last keydown interactionId is used to attribute simulated cancelable click to event ’s or cancelable contextmenu attribute value. Set timingEntry ’s events (which lack a interactionId keyCode to interactionId . Return timingEntry . 3.8. ), as well as Finalize event timing input events (which follow a keydown When asked ), back to finalize event timing , with timingEntry , event , target , and processingEnd as inputs, run the following steps: originating keyboard interaction.

  5. If Else if timingEntry type is null, then return. keypress :

    1. Let relevantGlobal keyCode be target event ’s relevant global object . keyCode attribute value.

    2. If relevantGlobal activeKeyInteractions does not implement Window , return. [ keyCode ] exists:

      1. Set timingEntry newInteractionId ’s processingEnd to processingEnd . activeKeyInteractions [ keyCode ].

    3. Assert that Else if target window implements Node ’s last keydown interactionId . is not 0:

      Note: This assertion holds due
      1. Set newInteractionId to the types of events supported by the Event Timing API. window ’s last keydown interactionId .

    4. Else:

      1. Set timingEntry newInteractionId ’s eventTarget to target . 0.

    Note: This will set eventTarget to the last event target. So if retargeting fallback for keypress occurs, the last target, closest to events is necessary because the root , will be used. Set timingEntry ’s targetSelector keyCode to the result of running the algorithm to generate a CSS selector keypress with target as input. If event ’s might differ from that of the preceding type keydown attribute value is event. [UIEVENTS] recommends using the pointerdown code : attribute to avoid such inconsistencies (see UI Events section on code motivation ).

  6. Let pendingPointerDowns be Else if relevantGlobal type ’s pending pointer downs . is keyup :

    1. Let pointerId keyCode be event ’s pointerId keyCode . attribute value.

    2. If pendingPointerDowns activeKeyInteractions [ pointerId keyCode ] exists:

      1. Let Set previousPointerDownEntry newInteractionId be to pendingPointerDowns activeKeyInteractions [ pointerId keyCode ].

    3. Add previousPointerDownEntry to Else:

      1. Set relevantGlobal newInteractionId ’s entries to be queued . 0.

    4. Set pendingPointerDowns [ pointerId window ] ’s last keyup interactionId to timingEntry newInteractionId .

    5. Set relevantGlobal window ’s is contextmenu triggered last keydown interactionId to false. 0.

  7. Otherwise, Else if event ’s type attribute value is keydown input :

    1. If event window ’s isComposing last keydown interactionId attribute value is true : not 0:

      1. Append Set timingEntry newInteractionId to relevantGlobal window ’s entries to be queued last keydown interactionId .

      2. Return. Set window ’s last keydown interactionId to 0.

    2. Let Else:

      1. Set pendingKeyDowns newInteractionId be to the result of getting the next interactionId given relevantGlobal ’s pending key downs . window .

  8. Let code be Else if event type ’s is keyCode click attribute value. or contextmenu :

    1. If pendingKeyDowns [ code window ] exists: ’s last keydown interactionId is not 0:

      1. Let previousKeyDownEntry be Set pendingKeyDowns newInteractionId [ to code window ]. ’s last keydown interactionId .

    2. If Else if code window ’s last keyup interactionId is not 229: 0:

      1. Increase relevantGlobal ’s user interaction value value by a small number chosen by the user agent. Set previousKeyDownEntry newInteractionId ’s interactionId to relevantGlobal window ’s user interaction value last keyup interactionId .

      Note: 229
    3. If newInteractionId is a special case since it corresponds to IME keyboard events. Sometimes multiple of these are sent by the user agent, and they do not correspond to holding a key down repeatedly. null:

      1. Add previousKeyDownEntry to Set relevantGlobal window ’s entries last keydown interactionId to be queued . 0.

      2. Set pendingKeyDowns [ code window ] ’s last keyup interactionId to timingEntry . 0.

    4. Otherwise: Else:

      1. Append Set timingEntry newInteractionId to the result of getting the next interactionId given relevantGlobal ’s entries window .

    Note: For click events, it is expected that the keyCode represents "Enter" or "Space", and an implementation can enforce this constraint.

  9. Return newInteractionId .

Note: During a composition session, keyboard and input events are still expected to be queued dispatched according to the UI Events section on keyboard events during composition . These events are assigned an interactionId corresponding to the active keyboard interaction, whereas the composition events themselves (e.g., compositionstart , compositionupdate ) are not assigned an interactionId .

3.9. 4.5. Dispatch pending Flush Event Timing entries

When asked to dispatch pending
To Flush Event Timing entries for given a Document doc , run the following steps: :
  1. Let window be doc ’s relevant global object .

  2. Let While renderingTimestamp window be the current high resolution time . ’s pending Event Timing entries is not empty:

    1. For each Let timingEntry in be the first element of window ’s pending Event Timing entries to be queued : .

    2. Set event timing entry duration passing If timingEntry , window , and renderingTimestamp . ’s interactionId is null, break.

    3. If timingEntry window ’s duration has queued first-input attribute value is greater than or equal to 16, then queue false, and timingEntry . ’s interactionId is not 0, run the following steps:

      1. Set window ’s entries to be has queued first-input to an empty list. true.

      2. For each Let pendingPointerDownEntry firstInputEntry in the values from be a copy of window timingEntry .

      3. Set firstInputEntry ’s pending pointer downs : entry type to " first - input ".

      4. Set event timing entry duration queue passing pendingPointerDownEntry , window , and renderingTimestamp firstInputEntry .

    4. For each pendingKeyDownEntry in the values from If window timingEntry ’s pending key downs : Set event timing entry duration passing is greater than or equal to 16, then queue pendingKeyDownEntry , timingEntry .

    5. Remove the first element of window , and renderingTimestamp . ’s pending Event Timing entries .

When asked to set event timing entry duration given a

4.6. Should add PerformanceEventTiming timingEntry ,

Note: The following algorithm is used in the [PERFORMANCE-TIMELINE-2] specification to determine when a Window PerformanceEventTiming window , and entry needs to be added to the buffer of a DOMHighResTimeStamp PerformanceObserver renderingTimestamp , perform or to the following steps: If timingEntry ’s performance timeline, as described in the registry .

To should add PerformanceEventTiming given a duration PerformanceEventTiming attribute value is nonzero, return. Let start be timingEntry entry ’s and an optional startTime PerformanceObserverInit attribute value. options :
  1. Set If timingEntry entry ’s duration entryType attribute value equals to a DOMHighResTimeStamp resulting from " renderingTimestamp first - start , with granularity of 8ms or less. input ", return true.

  2. Let name be Assert that timingEntry entry ’s name entryType attribute value. Perform the following steps to update the value equals " event counts: ".

  3. Let eventCounts minDuration be window ’s eventCounts . Assert that eventCounts contains name . Set eventCounts [ name ] to eventCounts [ name ] + 1. computed as follows:

    1. If window options ’s has dispatched input event is false, and not present or if timingEntry options ’s interactionId durationThreshold is not 0, run the following steps: Let present, let firstInputEntry minDuration be a copy of timingEntry . 104.

    2. Set Otherwise, let firstInputEntry minDuration be the maximum between 16 and options ’s entryType durationThreshold to " first - input ". queue firstInputEntry . value.

  4. Set If window entry ’s has dispatched input event duration attribute value is greater than or equal to minDuration , return true.

  5. Otherwise, return false.

3.10. 4.7. Target Selectors

To generate a CSS selector given an EventTarget target , run the following steps:
  1. If target is a Node , run the following steps:

    1. Let selector be a string with an initial value of target ’s nodeName .

    2. If target is an Element , run the following steps:

      1. If target has an `id` id attribute, set selector to the concatenation of « selector , "#", the value of the `id` attribute ».

      2. Otherwise, if target has a `src` src attribute, set selector to the concatenation of « selector , "[src=", the value of the `src` attribute, "]" ».

    3. Return selector .

  2. Otherwise, return an empty string.

4. 5. Security & privacy considerations

We would not like to introduce more high resolution timers to the web platform due to the security concerns entailed by such timers. Event handler timestamps have the same accuracy as performance.now() . Since processingStart and processingEnd could be computed without using this API, exposing these attributes does not produce new attack surfaces. Thus, duration is the only one which requires further consideration.

The duration has an 8 millisecond granularity (it is computed as such by performing rounding). Thus, a high resolution timer cannot be produced from these timestamps. However, it does introduce new information that is not readily available to web developers: the time pixels draw after an event has been processed. We do not find security or privacy concerns with exposing the timestamp, especially given its granularity. In an effort to expose the minimal amount of new information that is useful, we decided to pick 8 milliseconds as the granularity. This allows relatively precise timing even for 120Hz displays.

The choice of 104ms as the default cutoff value for the duration is just the first multiple of 8 greater than 100ms. An event whose rounded duration is greater than or equal to 104ms will have its pre-rounded duration greater than or equal to 100ms. Such events are not handled within 100ms and will likely negatively impact user experience.

The choice of 16ms as the minimum value allowed for durationThreshold is because it enables the typical use-case of making sure that the response is smooth. In 120Hz displays, a response that skips more than a single frame will be at least 16ms, so the entry corresponding to this user input will be surfaced in the API under the minimum value.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class = "example" , like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class = "note" , like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard . Living Standard. URL: https://dom.spec.whatwg.org/
[HR-TIME-2]
Ilya Grigorik. High Resolution Time Level 2 . URL: https://w3c.github.io/hr-time/
[HR-TIME-3]
Yoav Weiss. High Resolution Time . URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard . Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard . Living Standard. URL: https://infra.spec.whatwg.org/
[PAINT-TIMING]
Ian Clelland; Noam Rosenthal. Paint Timing . URL: https://w3c.github.io/paint-timing/
[PERFORMANCE-TIMELINE-2]
Nicolas Pena Moreno. Performance Timeline . URL: https://w3c.github.io/performance-timeline/
[POINTEREVENTS4]
Patrick Lauke; Robert Flack. Pointer Events . URL: https://w3c.github.io/pointerevents/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels . March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[TOUCH-EVENTS]
Doug Schepers; et al. Touch Events . URL: https://w3c.github.io/touch-events/
[UIEVENTS]
Xiaoqian Wu. UI Events . URL: https://w3c.github.io/uievents/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard . Living Standard. URL: https://webidl.spec.whatwg.org/

IDL Index

[Exposed=Window]
interface PerformanceEventTiming : PerformanceEntry {
    ;
    ;
    ;

    readonly attribute DOMHighResTimeStamp processingStart;
    readonly attribute DOMHighResTimeStamp processingEnd;
    readonly attribute boolean cancelable;
    readonly attribute Node? target;
    ;
    ;

    readonly attribute DOMString targetSelector;
    readonly attribute unsigned long long interactionId;
    [Default] object toJSON();
};
[Exposed=Window]
interface EventCounts {
    readonly maplike<DOMString, unsigned long long>;
};
[Exposed=Window]
partial interface Performance {
    [SameObject] readonly attribute EventCounts eventCounts;
    readonly attribute unsigned long long interactionCount;
};
partial dictionary PerformanceObserverInit {
    DOMHighResTimeStamp durationThreshold;
};
MDN

EventCounts

Firefox 89+ Safari None Chrome 85+
Opera ? Edge 85+
Edge (Legacy) ? IE None
Firefox for Android ? iOS Safari ? Chrome for Android ? Android WebView ? Samsung Internet ? Opera Mobile ?
MDN

Performance/eventCounts

Firefox 89+ Safari None Chrome 85+
Opera ? Edge 85+
Edge (Legacy) ? IE None
Firefox for Android ? iOS Safari ? Chrome for Android ? Android WebView ? Samsung Internet ? Opera Mobile ?
Node.js None
MDN

PerformanceEventTiming/cancelable

Firefox 89+ Safari None Chrome 76+
Opera ? Edge 79+
Edge (Legacy) ? IE None
Firefox for Android ? iOS Safari ? Chrome for Android ? Android WebView ? Samsung Internet ? Opera Mobile ?
MDN

PerformanceEventTiming/interactionId

In only one current engine.

Firefox None Safari None Chrome 96+
Opera ? Edge 96+
Edge (Legacy) ? IE None
Firefox for Android ? iOS Safari ? Chrome for Android ? Android WebView ? Samsung Internet ? Opera Mobile ?
MDN

PerformanceEventTiming/processingEnd

Firefox 89+ Safari None Chrome 76+
Opera ? Edge 79+
Edge (Legacy) ? IE None
Firefox for Android ? iOS Safari ? Chrome for Android ? Android WebView ? Samsung Internet ? Opera Mobile ?
MDN

PerformanceEventTiming/processingStart

Firefox 89+ Safari None Chrome 76+
Opera ? Edge 79+
Edge (Legacy) ? IE None
Firefox for Android ? iOS Safari ? Chrome for Android ? Android WebView ? Samsung Internet ? Opera Mobile ?
MDN

PerformanceEventTiming/target

Firefox 89+ Safari None Chrome 85+
Opera ? Edge 85+
Edge (Legacy) ? IE None
Firefox for Android ? iOS Safari ? Chrome for Android ? Android WebView ? Samsung Internet ? Opera Mobile ?
MDN

PerformanceEventTiming/toJSON

Firefox 89+ Safari None Chrome 76+
Opera ? Edge 79+
Edge (Legacy) ? IE None
Firefox for Android ? iOS Safari ? Chrome for Android ? Android WebView ? Samsung Internet ? Opera Mobile ?
MDN

PerformanceEventTiming

Firefox 89+ Safari None Chrome 76+
Opera ? Edge 79+
Edge (Legacy) ? IE None
Firefox for Android ? iOS Safari ? Chrome for Android ? Android WebView ? Samsung Internet ? Opera Mobile ?