Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
This gist pokes around with interactions in the EventTiming API. It tries to get the interaction latency, delay, processing time breakdown, type, and target.
const interactionMap = new Map();
function logInteraction(interaction) {
const clamp = val => Math.round(val * 100) / 100; // clamp to 2 decimal places
console.groupCollapsed(`${interaction.type} interaction`, clamp(interaction.latency));
console.log(`total latency`, clamp(interaction.latency));
console.log('delay:', clamp(interaction.delay));
console.groupCollapsed(`processing time in ${Object.entries(interaction.processingTimes).length} entries:`, clamp(interaction.processingTime));
for (const [e, t] of Object.entries(interaction.processingTimes)) {
console.log(clamp(t), `in ${e}`);
}
console.groupEnd();
console.log(`time to next paint: `, clamp(interaction.latency - (interaction.delay + interaction.processingTime)));
console.log('interacted with: %O', interaction.target);
console.groupEnd();
}
function getInteractionType(eventName) {
// From https://web.dev/better-responsiveness-metric/#interaction-types
if (['keydown', 'keypress'].includes(eventName)) {
return 'Key pressed';
} else if (['keyup'].includes(eventName)) {
return 'Key released';
} else if (['pointerdown', 'touchstart'].includes(eventName)) {
return 'Tap start or drag start';
} else if (['pointerup', 'mouseup', 'touchend', 'click'].includes(eventName)) {
return 'Tap up or drag end';
}
else if (eventName == 'mousedown') {
if(/Android/i.test(navigator.userAgent)) {
return 'Tap up or drag end';
} else {
return 'Tap start or drag start';
}
}
return null;
}
new PerformanceObserver((entries) => {
let newInteractions = [];
for (const entry of entries.getEntries()) {
// Ignore entries without an interaction ID.
if (entry.interactionId > 0) {
// Get the interaction for this entry, or create one if it doesn't exist.
const interactionType = getInteractionType(entry.name);
const interactionId = entry.interactionId + ' - ' + interactionType;
let interaction = interactionMap.get(interactionId);
if (!interaction) {
interaction = {
latency: entry.duration,
delay: entry.processingStart - entry.startTime,
target: entry.target,
processingTimes: {}
};
interactionMap.set(interactionId, interaction);
newInteractions.push(interactionId);
}
if (interactionType) {
interaction.type = interactionType;
}
entryDelay = entry.processingStart - entry.startTime;
if (entryDelay < interaction.delay) {
interaction.delay = entryDelay;
}
interaction.processingTimes[entry.name] = entry.processingEnd - entry.processingStart;
interaction.processingTime = Object.values(interaction.processingTimes).reduce((a, b) => a + b);
if (entry.duration > interaction.latency) {
interaction.latency = entry.duration;
}
}
}
for (const newInteraction of newInteractions) {
logInteraction(interactionMap.get(newInteraction));
}
// Set the `durationThreshold` to 50 to capture keyboard interactions
// that are over-budget (the default `durationThreshold` is 100).
}).observe({type: 'event', buffered: true, durationThreshold: 50});
@anniesullie
Copy link
Author

anniesullie commented Dec 10, 2021

Awesome! Thanks so much! Updated mine to include the better logging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment