I have an element with a "pointerMove" EventListener added to it. Now when moving my mouse around, I can measure the number of data points "pointerMove" delivers per second (pps) by counting the total number of points drawn since "pointerDown" and dividing this by the time that passed since "pointerDown". So far, so good. What is strange though is the fact that I get a higher pps rate when the developer console is opened.
Example: Pressing my mouse button and then moving the cursor around chaotically gives me about 60pps. But when opening the developer console and then doing exactly the same, my pps rises to about 235 - almost 400% increase!
This test was done in Chrome 76 on Windows 10. Similar results may be obtained using Firefox. This issue also concerns input via touch or pen and is present in Chrome OS as well (behaviour on other operating systems has not yet been examined). Interestingly though Microsoft Edge seems not to be affected.
So the question is: Why does this happen? And how may I get the higher number of pps without having to open the developer console?
Executable example here: /
var pointerid = undefined;
var start, count;
var canvas;
function startup() {
canvas = document.getElementById("canvas");
canvas.addEventListener("pointerdown", pointerdown, false);
canvas.addEventListener("pointermove", pointermove, false);
canvas.addEventListener("pointerup", pointerup, false);
canvas.addEventListener("pointercancel", pointerup, false);
canvas.addEventListener("touchstart", touch, false);
canvas.addEventListener("touchmove", touch, false);
canvas.addEventListener("touchend", touch, false);
canvas.addEventListener("touchcancel", touch, false);
}
function pointerdown(event) {
event.preventDefault();
rect = canvas.getBoundingClientRect();
if ((event.pointerType == "pen" || event.pointerType == "mouse") && pointerid == undefined) {
pointerid = event.pointerId;
var x = event.clientX - rect.left,
y = event.clientY - rect.top;
start = new Date().getTime();
count = 1;
}
}
function pointermove(event) {
event.preventDefault();
if (pointerid == event.pointerId) {
var x = event.clientX - rect.left,
y = event.clientY - rect.top;
count++;
}
}
function pointerup(event) {
event.preventDefault();
if (pointerid == event.pointerId) {
var x = event.clientX - rect.left,
y = event.clientY - rect.top;
pointerid = undefined;
count++;
console.log((count / (new Date().getTime() - start) * 1000) + "pps");
}
}
function touch(event) {
if (pointerid != undefined) {
event.preventDefault();
}
}
startup();
<div id="canvas" style="width:2000px; height:2000px; ">
</div>
I have an element with a "pointerMove" EventListener added to it. Now when moving my mouse around, I can measure the number of data points "pointerMove" delivers per second (pps) by counting the total number of points drawn since "pointerDown" and dividing this by the time that passed since "pointerDown". So far, so good. What is strange though is the fact that I get a higher pps rate when the developer console is opened.
Example: Pressing my mouse button and then moving the cursor around chaotically gives me about 60pps. But when opening the developer console and then doing exactly the same, my pps rises to about 235 - almost 400% increase!
This test was done in Chrome 76 on Windows 10. Similar results may be obtained using Firefox. This issue also concerns input via touch or pen and is present in Chrome OS as well (behaviour on other operating systems has not yet been examined). Interestingly though Microsoft Edge seems not to be affected.
So the question is: Why does this happen? And how may I get the higher number of pps without having to open the developer console?
Executable example here: https://jsfiddle/Galveston01/unxmrchw/
var pointerid = undefined;
var start, count;
var canvas;
function startup() {
canvas = document.getElementById("canvas");
canvas.addEventListener("pointerdown", pointerdown, false);
canvas.addEventListener("pointermove", pointermove, false);
canvas.addEventListener("pointerup", pointerup, false);
canvas.addEventListener("pointercancel", pointerup, false);
canvas.addEventListener("touchstart", touch, false);
canvas.addEventListener("touchmove", touch, false);
canvas.addEventListener("touchend", touch, false);
canvas.addEventListener("touchcancel", touch, false);
}
function pointerdown(event) {
event.preventDefault();
rect = canvas.getBoundingClientRect();
if ((event.pointerType == "pen" || event.pointerType == "mouse") && pointerid == undefined) {
pointerid = event.pointerId;
var x = event.clientX - rect.left,
y = event.clientY - rect.top;
start = new Date().getTime();
count = 1;
}
}
function pointermove(event) {
event.preventDefault();
if (pointerid == event.pointerId) {
var x = event.clientX - rect.left,
y = event.clientY - rect.top;
count++;
}
}
function pointerup(event) {
event.preventDefault();
if (pointerid == event.pointerId) {
var x = event.clientX - rect.left,
y = event.clientY - rect.top;
pointerid = undefined;
count++;
console.log((count / (new Date().getTime() - start) * 1000) + "pps");
}
}
function touch(event) {
if (pointerid != undefined) {
event.preventDefault();
}
}
startup();
<div id="canvas" style="width:2000px; height:2000px; ">
</div>
Share
Improve this question
edited Aug 29, 2019 at 14:53
Galveston01
asked Aug 29, 2019 at 13:40
Galveston01Galveston01
3662 silver badges13 bronze badges
2
- Can't repro in FF69 on macOS (60PPS constant), Can repro in Chrome76 (60PPS when dev-tools off, ~120 when on). Can't repro on Safari (~120 constant). Note that it's not needed to use PointerEvent, MouseEvent expose the same behavior: jsfiddle/y1nkehbL I think I know what happens, but misses time to dig sources, so here is the gist: Browsers (at least FF and Chrome) do throttle some UI events, like mousemove and probably others to the screen refresh rate. Chrome has a bug which make this throttling deactivated when the dev-tools are on. – Kaiido Commented Aug 29, 2019 at 14:27
- 1 I need to use PointerEvent since my main purpose is to detect pen input. Unfortunately, you can really see a huge difference between handdrawn lines with 60pps vs 240pps (especially when drawing quickly). Any way to exploit the Chrome bug so that throttling is always deactivated? And how does it happen that I'm getting the same effect in FF although it doesn't have such a bug? Or does it too? – Galveston01 Commented Aug 29, 2019 at 14:49
1 Answer
Reset to default 5Specs now encourage browser vendors to threshold most UI Events in order to improve performances.
For instance you can find such a notice in mousemove UI-Event specs:
Implementations are encouraged to determine the optimal frequency rate to balance responsiveness with performance.
And about the same in PointerEvents' pointermove specs drafts
These events may be coalesced or aligned to animation frame callbacks based on UA decision.
In the facts, both Firefox and Chrome do currently coalesce these events in animation frame callback i.e it's actually aligned on your monitor's refresh rate.
This also means that for pointermove we can retrieve all these events using the PointerEvent.getCoalescedEvents
method.
const canvas = document.getElementById("canvas");
let
count = 0,
start = 0;
const opts = {passive: true};
canvas.addEventListener("pointerdown", pointerdown, opts);
canvas.addEventListener("pointermove", pointermove, opts);
canvas.addEventListener("pointerup", pointerup, opts);
canvas.addEventListener("touchstart", prevent);
canvas.addEventListener("touchmove", prevent);
function pointermove(event) {
if(canvas.hasPointerCapture(event.pointerId)) {
const coalesced = event.getCoalescedEvents();
count += coalesced.length;
points.push(...coalesced);
draw();
}
}
function pointerdown(event) {
canvas.setPointerCapture(event.pointerId);
count = 1;
start = new Date().getTime();
points.length = 0;
}
function pointerup(event) {
if(canvas.hasPointerCapture(event.pointerId)) {
canvas.releasePointerCapture(event.pointerId);
count++;
const PPS = (count / (new Date().getTime() - start) * 1000);
log.textContent = PPS + "pps";
}
}
// just to show we have real Events
const ctx = canvas.getContext('2d');
const points = [];
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
points.forEach(evt => {
ctx.lineTo(evt.offsetX, evt.offsetY);
});
ctx.stroke();
}
// prevent default touch events or pointer ones are discarded
function prevent(evt) {
evt.preventDefault();
}
#canvas {
border: 1px solid;
}
<pre id="log"></pre>
<canvas id="canvas" width="2000" heigth="2000"></canvas>
Note that in Chrome these Events do have their own timestamp
, so you can know when they should have fired, but in Firefox this property is set to 0
...
However, a pointerrawupdate event is ing in the drafts and is already available in Chrome under the Experimental Web Platform features flag.
This event will not be aligned to animation frames and instead will fire "as soon as possible".
if(!('onpointerrawupdate' in window)) {
console.error("Your browser doesn't support 'pointerrawupdate' event. You may need to toggle some config flags");
}
else {
const canvas = document.getElementById("canvas");
let
count = 0,
start = 0;
const opts = {passive: true};
canvas.addEventListener("pointerdown", pointerdown, opts);
canvas.addEventListener("pointerrawupdate", pointermove, opts);
canvas.addEventListener("pointerup", pointerup, opts);
function pointermove(event) {
if(canvas.hasPointerCapture(event.pointerId)) {
const coalesced = event.getCoalescedEvents();
count += coalesced.length;
}
}
function pointerdown(event) {
canvas.setPointerCapture(event.pointerId);
count = 1;
start = new Date().getTime();
}
function pointerup(event) {
if(canvas.hasPointerCapture(event.pointerId)) {
canvas.releasePointerCapture(event.pointerId);
count++;
const PPS = (count / (new Date().getTime() - start) * 1000);
log.textContent = PPS + "pps";
}
}
// Removed the drawing part because drawing should be made in animation frames
}
#canvas {
border: 1px solid;
}
<pre id="log"></pre>
<canvas id="canvas" width="2000" heigth="2000"></canvas>
But in your case (a drawing app) you'd be better to stick with getCoalescedEvents
since your drawings should anyway happen only in animation frames.
Ps: about why the threshold is deactivated when dev-tools are open, that's probably a browser bug.