25.05.2025

Blurry Web Assembly program devicePixelRatio issue

(Updated 21.08.2025)

I compiled my C code to webassembly using emscripten, and the program was blurrier on the web. The reason is because css pixel size is not the same as the physical monitor pixel size. The Window: devicePixelRatio property affects the size things on a page.

This is a really obnoxious issue. Ultimately I have decided it is not worth trying to fixing because the blurriness is hard to notice and the solution depends on obscure browser variables. This article has an in depth look at how to handle this issue with webgl.

This is an attempt I made to try to fix it. It resizes the canvas so that the number of pixels is the same on the browser as it would be on the desktop. On screens with high pixel density this makes the canvas element very small.

I have a canvas that my program draws to: <canvas id="canvas" style="width:600px;height:600px" width="600" height="600"> </canvas>

I am using sokol, so when I was resized in the browser it was changing canvas.width to equal canvas.style.width, so I had to set .html5_canvas_resize = true, so that the canvas.width won't change.

main_wasm.c:

sapp_desc sokol_main(int argc, char* argv[]) {
  (void)argc;(void)argv;

  return (sapp_desc){
    ...
    .html5_canvas_resize = true, // prevent resizing canvas
  }
}

emscripten accesses a global Module object, to do stuff when loading the wasm file.

wasm_module.js:


function ModuleLoaded() {
  const scale = window.devicePixelRatio; // depends on device setting. window scale and text size can affect this.

  canvas.style.width = (canvas.width/scale) + 'px';
  canvas.style.height = (canvas.height/scale) + 'px';
};

var Module = {
  preRun: [PreLoad],

  postRun: [ModuleLoaded],
  .
  .
  .
};

In my C code I get the initial devicePixelRatio

  F64 dpr = emscripten_get_device_pixel_ratio();
  dpr_initial = dpr;

and in order to get the mouse movement to work, I use that initial devicePixelRatio to scale the mouse position

        mouse_x = e->mouse_x * dpr_initial;
        mouse_y = e->window_height - (e->mouse_y*dpr_initial);

the devicePixelRatio changes when zooming the browser, but since I am not resizing the canvas I want this ratio to be true: canvas.width/canvas.style.width = dpr_initial