Во-первых у меня на устройстве с кратной плотностью пикселей подобных проблем с поведением нет. Вижу 1000 на 1000 канвас с сеткой. Ниже опишу какие нюансы надо учесть чтобы на других устройствах вглядело подобным образом
В коде есть пара нюансов которые мешают получить четкую сетку
Ширина линии должна быть целым (сейчас ширина 0,5 ctx.lineWidth = 0.5;). Используй целое. Все равно ты увеличиваешь размер канваса так чтобы пиксели канваса соответствовали физическим пикселям.
Я предполжу что ты хочешь достичь линий толщеной с физический пиксель (программный пиксель будет пропорционально толще). Такое выглядит круто. Для этого нужна комбинация из установления размеров канваса пропорционально DPR (device pixel ratio, отношение плотности пикселей (не знаю точный термин на русском)).
сделать ширину линии пропорциональной dpr
нарисовать на увеличенном канвасе
трансформировать-уменьшить размер канваса.
Математика вычисления размера канваса “перемешана”. Тут ты задаешь 500 пикселей can.width = 500;, и ниже задаешь новое значение can.width = can.offsetWidth * dpr;.
Думаю что мы за одну итерацию не дойдем до того что именно тебе надо. Доспрашивай, доуточняй
Конечный результат (канвас на 500 программных пикселей с сеткой линий толщиной в физический пиксель):