Skip to content
Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Collect and measure browser performance metrics

All metrics are converted to snake_case

import { navigation, paint } from 'page-timing';

(async () => {
    const results = await Promise.all([

    const metrics = Object.assign(...results);

    fetch('/browser-performance-metrics', {
        method: 'POST',
        body: JSON.stringify({
            page_name: 'my page',

API endpoints


Name Meaning Group Type
navigation_start Termination of previous document upon navigating navigation number
unload_event_start Previous document unload navigation number
unload_event_end navigation number
redirect_count Numbers of redirects while requesting this page navigation number
redirect_start Redirect from previous document navigation number
redirect_end navigation number
fetch_start Ready to fetch the document navigation number
domain_lookup_start navigation number
domain_lookup_end navigation number
duration Difference between responseEnd and startTime navigation number
connect_start Sent request to open a connection navigation number
connect_end navigation number
secure_connection_start Secure connection handshake navigation number
request_start Request the document navigation number
response_start Received the first byte of the response navigation number
response_end Received the last byte of the response navigation number
dom_loading Parser started work navigation number
dom_interactive Parser finished work on main document. Changed document readyState to "interactive" navigation number
dom_content_loaded_event_start Executed required scripts after parsing the document navigation number
dom_content_loaded_event_end navigation number
dom_complete Changed document readyState to "complete" navigation number
load_event_start All assets are loaded. Document fires "load" event navigation number
load_event_end Document finished executing "load" event listeners navigation number
transfer_size Size (octets) of response headers and payload body navigation number
encoded_body_size Size (octets) of payload body navigation number
decoded_body_size Size (octets) of message body navigation number
worker_start Time until service worker ran navigation number
first_paint User agent first rendered after navigation paint number
first_contentful_paint Document contains at least one element that is paintable and contentful † paint number
first_image_paint TBD paint number
final_asset_javascript_count Total number of Javascript resources assets number
final_asset_javascript_load Loading time spent on Javascript resources assets number
final_asset_javascript_size Total size of Javascript resources assets number
final_asset_stylesheets_count Total number of CSS resources assets number
final_asset_stylesheets_load Loading time spent on CSS resources assets number
final_asset_stylesheets_size Total size of CSS resources assets number
final_asset_images_count Total number of image resources assets number
final_asset_images_load Loading time spent on image resources assets number
final_asset_images_size Total size of image resources assets number
final_asset_other_count Total number of other resources assets number
final_asset_other_load Loading time spent on other resources assets number
final_asset_other_size Total size of other resources assets number
connection_type bluetooth, cellular, ethernet, none, wifi, wimax, other, unknown connection string
effective_bandwidth Mbps connection number
effective_connection_type slow-2g, 2g, 3g, 4g connection string
effective_max_bandwidth Mbps connection number
reduced_data_usage Vendor's "Data Saver" feature enables connection boolean
round_trip_time Estimated effective round-trip in ms connection number
navigation_type navigate, reload, back_forward, prerender connection string
js_heap_size_limit Maximum bytes available for JS heap memory number
total_js_heap_size Total allocated bytes for JS heap memory number
used_js_heap_size Currently active bytes of JS heap memory number
window_inner_height Height of the window's layout viewport display number
window_inner_width Width of the window's layout viewport display number
screen_color_depth Color depth of the screen display number
screen_pixel_depth Bit depth of the screen display number
screen_orientation_type landscape-primary, landscape-secondary, portrait-primary, portrait-secondary display string
final_dom_node_count Total number of nodes under the document object dom number
final_dom_nest_depth Highest nesting depth of DOM element under the document dom number
final_html_size Character count of the HTML document dom number
page_time_elapsed milliseconds elapsed since the time origin elapsed number

contentful element: A visible element which contains non empty text, media content or input.

More functions


Measure page frame rate at a certain point in time

import { fps } from 'page-timing';

const frames_per_second = await fps();
console.log({ frames_per_second });

Increase sample rate by checking more than one second. (Result is still in frames per second)

const frames_per_second = await fps({ sample: 5 });
console.log({ frames_per_second });


Wrap a function and measure it's execution time in milliseconds into a performance measure entry.

import { measure } from 'page-timing';

async function myFunction(
    await wait(50);

await measure(myFunction, 'my-function');

// Example: Convert entries to a named array
        ({ name, duration }) => ({[name]: duration})
// {my-function: 53.35999990347773}

// Example: Retrieve a specific entry
const { duration } = performance.getEntriesByName('my-function');
// 53.35999990347773

Illustration of navigation events


A simple example to add web vitals and TTI

npm i page-timing web-vitals tti-polyfill
import { all, connection } from 'page-timing';
import { getLCP, getFID, getCLS } from 'web-vitals';
import TTI from 'tti-polyfill';

(async () => {
    const connectionInfo = await connection();

    // Send metrics from browser performance API
    send(await all());

    // Send web vitals to the same endpoint
        [getLCP, 'largest_contentful_paint'],
        [getFID, 'first_input_delay'],
        [getCLS, 'cumulative_layout_shift'],
        ([ fn, name ]) => fn(
            ({ value }) => send({
                [name]: value,
                ...connectionInfo // Some connection info

        (time_to_interactive) => send({
            ...connectionInfo // Some connection info
    ).catch(() => null)

const send = metrics => fetch('/browser-performance-metrics', {
  method: 'POST',
  body: JSON.stringify({ page_name: 'my page', metrics }),