1. Introduction
This section is non-normative.
Much of the purpose of a web browser is to translate HTML, CSS and image resources into pixels on a screen for users. Measuring the performance of a web page often involves measuring the time it takes to perform these tasks - to render content, whether text or image, to the screen. There are many different ways to use this timing to make statemements about the performance of a page, or about the user experience of loading, but fundamentally all of those ways begin with a common means of measuring time.
This is a foundational document which specifies how to measure paint timing as a general-purpose mechanism. That foundation is then used to define the First Paint and First Contentful Paint metrics. Other specific instances of paint measurement may be specified in other documents.
Specifically, this specification covers:
-
Measuring the time when images are decoded and ready for painting
-
Measuring the time when elements are painted
-
Measuring the size of the painted elements
-
Determining whether a painted element contains any visible content.
1.1. First Paint and First Contentful Paint
Load is not a single moment in time — it’s an experience that no one metric can fully capture. There are multiple moments during the load experience that can affect whether a user perceives it as "fast" or "slow".
First paint (FP) is the first of these key moments, followed by first contentful paint (FCP). These metrics mark the points in time when the browser renders a given document. This is important to the user because it answers the question: is it happening?
The primary difference between the two metrics is FP marks the first time the browser renders anything for a given document. By contrast, FCP marks the time when the browser renders the first bit of image or text content from the DOM.
1.2. Usage example
const observer= new PerformanceObserver( function ( list) { const perfEntries= list. getEntries(); for ( const perfEntryof perfEntries) { // Process entries // report back for analytics and monitoring // ... } }); // register observer for paint timing notifications observer. observe({ entryTypes: [ "paint" ]});
2. Terminology
Paint: the user agent has performed a "paint" (or "render") when it has converted the render tree to pixels on the screen. Formally, we consider the user agent to have "rendered" a document when it has performed the update the rendering steps of the event loop.
NOTE: The rendering pipeline is very complex, and the timestamp should be the latest timestamp the user agent is able to note in this pipeline (best effort). Typically the time at which the frame is submitted to the OS for display is recommended for this API.
A generated content pseudo-element is a paintable pseudo-element when all of the following apply:
-
The pseudo-element’s used visibility is
visible
. -
The pseudo-element generates a non-empty box.
A CSS image img is a contentful image when all of the following apply:
-
img is url valued.
-
img is available.
A DOMString
is non-empty if it contains at least one character excluding document white space characters.
An element target is contentful when one or more of the following apply:
-
target has a text node child, representing non-empty text, and the node’s used opacity is greater than zero.
NOTE: this covers the case where a typographical pseudo-element would override the opacity of the text node.
-
target is a replaced element representing an available image.
-
target has a background-image which is a contentful image, and its used background-size has non-zero width and height values.
-
target is a
canvas
with its context mode set to any value other thannone
. -
target is a
video
element that represents its poster frame or the first video frame and the frame is available. -
target is an svg element with rendered descendants.
-
target is an
input
element with a non-emptyvalue
attribute. -
target is an originating element for a paintable pseudo-element that represents a contentful image or non-empty text.
An element is timing-eligible if it is one of the following:
-
an
img
element. -
a
video
element with a poster frame. -
an element with a contentful background-image.
-
a text node.
To compute the paintable bounding rect of element target, run the following steps:
-
Let boundingRect be the result of running the
getBoundingClientRect()
on target. -
Clip boundingRect with the document's scrolling area.
-
Return boundingRect.
NOTE: elements contained by boxes with overflow
or overflow
don’t have their paintable bounding rect clipped, as in both cases the element can become visible by scrolling.
An element el is paintable when all of the following apply:
-
el is being rendered.
-
el’s used visibility is
visible
. -
el and all of its ancestors' used opacity is greater than zero.
NOTE: there could be cases where a
paintable
element would not be visible to the user, for example in the case of text that has the same color as its background. Those elements would still considered as paintable for the purpose of computing first contentful paint. -
el’s paintable bounding rect intersects with the scrolling area of the document.
NOTE: This covers the cases where the element is scaled to zero size, has
display
, or: nonedisplay
where the contents resolve to an empty rect.: contentsNOTE: As a general rule, an element is paintable if it is within the viewport, or can potentially be in the viewport as a result of scrolling or zooming.
First paint entry contains a DOMHighResTimeStamp
reporting the time when the user agent first rendered after navigation. This excludes the default background paint, but includes non-default background paint and the enclosing box of an iframe. This is the first key moment developers care about in page load – when the user agent has started to render the page.
A browsing context ctx is paint-timing eligible when one of the following apply:
-
ctx is a top-level browsing context.
-
ctx is a nested browsing context, and the user agent has configured ctx to report paint timing.
NOTE: this allows user agents to enable paint-timing only for some of the frames, in addition to the main frame, if they so choose. For example, a user agent may decide to disable paint-timing for cross-origin iframes, as in some scenarios their paint-timing might reveal information about the main frame.
The PaintTimingMixin
interface {#sec-PaintTimingMixin}
[Exposed =Window ]interface mixin {
PaintTimingMixin readonly attribute DOMHighResTimeStamp ;
paintTime readonly attribute DOMHighResTimeStamp ?; };
presentationTime
Objects including the PaintTimingMixin
interface mixin have an associated paint timing info (null or a paint timing info).
paint timing info is a struct. It has the following items:
- rendering update end time
- implementation-defined presentation time
-
Null or a
DOMHighResTimeStamp
The paintTime
attribute’s getter step is to return this's paint timing info's rendering update end time.
The presentationTime
attribute’s getter step, if exists, is to return this's paint timing info's implementation-defined presentation time.
To get the default paint timestamp for a paint timing info paintTimingInfo, return paintTimingInfo’s implementation-defined presentation time if it is non-null, otherwise paintTimingInfo’s rendering update end time.
3. The PerformancePaintTiming
interface
[Exposed =Window ]interface :
PerformancePaintTiming PerformanceEntry {};PerformancePaintTiming includes PaintTimingMixin ;
PerformancePaintTiming
extends the following attributes of PerformanceEntry
interface:
-
The
name
attribute’s getter must return aDOMString
for minimal frame attribution. Possible values of name are:-
: for first paint"first-paint" -
: for first contentful paint"first-contentful-paint"
-
-
The
entryType
attribute’s getter must return
."paint" -
The
startTime
attribute’s getter must return aDOMHighResTimeStamp
of when the paint occured. -
The
duration
attribute’s getter must return 0.
NOTE: A user agent implementing PerformancePaintTiming
would need to include
in supportedEntryTypes
of a global object whose browsing context is paint-timing eligible.
This allows developers to detect support for paint timing for a particular browsing context.
4. Processing model
4.1. Associated Image Requests
Each Element
has an associated image request which is an image
request or null, initially null.
When the processing model for an Element
element of type HTMLImageElement
, SVGImageElement
, or HTMLVideoElement
creates a
new image resource (e.g., to be displayed as an image or poster image), element’s associated image request is set to the image
request of the created image resource.
Note: Every image resource that is obtained from a URL whose scheme is equal to "data" has an associated image request which is not fetched but still needs to be loaded. This request can be the associated image request of an Element
.
Note: The current language is vague since it does not point to specific algorithms. This can be made more rigorous when the corresponding processing models have a more unified processing model.
Every Element
has a list of associated background image requests which is initially an empty array. When the processing model for the Element
element’s style requires a new image resource (to be displayed as
background image), the image request created by the new resource is
appended to element’s associated background image requests.
NOTE: An Element
can have several image requests, e.g. if its background-image property has multiple values. For instance, in the
following example, a single background-image property produces four image requests, each of which will be recorded and reported by the
algorithms below.
<!DOCTYPE html> < style > div { background-image : url( https://images.example/background1.png ), url( https://images.example/background2.png ); } </ style > < div ></ div > < div ></ div >
4.2. Recording paint timing
A pending image record is a struct with the following items:
-
element, an
Element
-
request, an image request
-
loadTime, a
DOMHighResTimeStamp
Each Element
has a set of owned text nodes, which is an ordered set of Text
nodes, initially empty.
Each Document
has a set of previously reported paints, which is an ordered set of strings, initially empty.
Each Document
has an images pending rendering, which is a list of pending image records, initally empty.
Each Document
has a set of elements with rendered text, which is an ordered set of Element
s, initially empty.
4.2.1. Modifications to the CSS specification
Whenever an image request in an Element
element’s associated background image requests becomes completely available, run the algorithm to process an image that finished loading with element and image request as inputs.
4.2.2. Modifications to the HTML specification
When an Element
element’s associated image request has become completely available, run the algorithm to process an image that finished loading passing in element and its associated image request as inputs.
Text
node text for the first time, it should execute the following steps:
-
If text will not be painted due to the font face being in its font block period, then return.
-
Let element be the
Element
which determines the containing block of text. -
Append text to element’s set of owned text nodes.
4.2.3. Process image that finished loading
Element
element and an image request imageRequest:
-
Let root be element’s root.
-
If root is not a
Document
, return. -
Let now be the current high resolution time given element’s relevant global object.
-
Let record be a pending image record with element element, request imageRequest and loadTime now.
-
Add record to root’s images pending rendering.
4.3. Reporting paint timing
4.3.1. First Contentful Paint
-
If document’s set of previously reported paints contains
, then return false."first-contentful-paint" -
If document contains at least one element that is both paintable and contentful, then return true.
-
Otherwise, return false.
4.3.2. Mark paint timing
-
If the document's browsing context is not paint-timing eligible, return.
-
Let paintTimingInfo be a new paint timing info, whose rendering update end time is the current high resolution time given document’s relevant global object.
-
Let paintedImages be a new ordered set
-
Let paintedTextNodes be a new ordered set
-
For each record in doc’s images pending rendering list:
-
If record’s request is available and ready to be painted, then run the following steps:
-
Append record to paintedImages.
-
Remove record from doc’s images pending rendering list.
-
-
-
For each
Element
element in doc’s descendants:-
If element is contained in doc’s set of elements with rendered text, continue.
-
If element’s set of owned text nodes is empty, continue.
-
Append element to doc’s set of elements with rendered text.
-
Append element to paintedTextNodes.
-
-
Let reportedPaints be the document’s set of previously reported paints.
-
Let flushPaintTimings be the following steps:
-
If reportedPaints does not contain
, and the user agent is configured to mark first paint, then report paint timing given document,"first-paint"
, and paintTimingInfo."first-paint" NOTE: First paint excludes the default background paint, but includes non-default background paint.
-
If document should report first contentful paint, then:
-
Report paint timing given document,
, and paintTimingInfo."first-contentful-paint"
NOTE: A parent frame should not be aware of the paint events from its child iframes, and vice versa. This means that a frame that contains just iframes will have first paint (due to the enclosing boxes of the iframes) but no first contentful paint.
NOTE: A document is not guaranteed to mark
or"first-paint"
. A completely blank document may never mark first paint, and a document containing only elements that are not contentful may never mark first contentful paint."first-contentful-paint" NOTE: The marking of first paint is optional. User-agents implementing paint timing should at the very least mark first contentful paint.
-
-
Report largest contentful paint given document, paintTimingInfo, paintedImages and paintedTextNodes.
-
Report element timing given document, paintTimingInfo, paintedImages and paintedTextNodes.
-
-
If the user-agent does not support implementation-defined presentation times, call flushPaintTimings and return.
-
Run the following steps In parallel:
-
Wait until an implementation-defined time when the current frame has been presented to the user.
-
Set paintTimingInfo’s implementation-defined presentation time to the current high resolution time given document’s relevant global object.
-
If document’s cross-origin isolated capability is false, then:
-
Coarsen paintTimingInfo’s implementation-defined presentation time to the next multiple of 4 milliseconds, or coarser.
-
Wait until the current high resolution time is paintTimingInfo’s implementation-defined presentation time.
-
-
Queue a global task on the performance timeline task source given document’s relevant global object to run flushPaintTimings.
-
4.3.3. Report paint timing
-
Create a new
PerformancePaintTiming
object newEntry with document’s relevant realm and set its attributes as follows:-
Set newEntry’s
name
attribute to paintType. -
Set newEntry’s
entryType
attribute to
."paint" -
Set newEntry’s
startTime
attribute to the default paint timestamp given paintTimingInfo. -
Set newEntry’s
duration
attribute to 0.
-
-
Set newEntry’s paint timing info to paintTimingInfo.
-
Queue newEntry in document’s relevant realm.
-
Append paintType to document’s set of previously reported paints.
4.4. Common algorithms
4.4.1. Exposed for paint timing
To determine whether an Element element is exposed for paint timing, given a Document or null document, perform the following steps:
-
If element is not connected, return false.
-
If document is null, let document be element’s relevant settings object's relevant global object's associated document.
-
If document is not fully active, return false.
-
If element’s root is not equal to document, return false.
-
Return true.
5. Acknowledgements
Special thanks to all the contributors for their technical input and suggestions that led to improvements to this specification.