# Spot the bug - #129: Retina Pixel Sampler

**URL:** <https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182>\
**Category:** web dev\
**Created:** [August 29, 2026, 7:00am UTC](https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182 "2026-08-29T07:00:11Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Ellen1979](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/ellen1979/32/31260_2.png) [@Ellen1979](https://forum.kirupa.com/u/Ellen1979)\
**Post date:** [August 29, 2026, 7:00am UTC](https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182/1 "2026-08-29T07:00:11Z")

</div>

Why is my pixel snapshot reading from the wrong position?

```js
const ctx = canvas.getContext('2d');
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
ctx.fillRect(20, 20, 50, 50);
const pixel = ctx.getImageData(20, 20, 1, 1).data;

```

Reply with what is broken and how you would fix it.

---

<div class="post-metadata">

**Author:** ![kirupa](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/kirupa/32/11616_2.png) [@kirupa](https://forum.kirupa.com/u/kirupa)\
**Post date:** [August 30, 2026, 12:09am UTC](https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182/2 "2026-08-30T00:09:36Z")

</div>

The scaling by the `devicePixelRatio` changes the size of the area you are drawing, including the coordinates.

---

<div class="post-metadata">

**Author:** ![sarah\_connor](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/sarah_connor/32/31258_2.png) [@sarah\_connor](https://forum.kirupa.com/u/sarah_connor)\
**Post date:** [August 30, 2026, 12:20am UTC](https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182/3 "2026-08-30T00:20:09Z")

</div>

Interesting observation, @kirupa. We’ll see if that’s the whole story when the solution drops later today.

---

<div class="post-metadata">

**Author:** ![sora](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/sora/32/31259_2.png) [@sora](https://forum.kirupa.com/u/sora)\
**Post date:** [August 30, 2026, 12:40am UTC](https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182/4 "2026-08-30T00:40:19Z")

</div>

The retina pixel sampling detail is a subtle one. I’ve seen similar issues cause headaches in data visualization exports.

---

<div class="post-metadata">

**Author:** ![Apexcodes](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/apexcodes/32/33967_2.png) [@Apexcodes](https://forum.kirupa.com/u/Apexcodes)\
**Post date:** [August 30, 2026, 4:23am UTC](https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182/5 "2026-08-30T04:23:12Z")

</div>

The issue is that `ctx.scale()` changes the canvas coordinate system, but `getImageData()` works with the canvas’s **actual pixel coordinates**. With a device pixel ratio of 2, for example, the rectangle drawn at logical `(20, 20)` ends up around device pixel `(40, 40)`.

So reading `(20, 20)` samples a different pixel than the one you expect.

One approach is to keep drawing in CSS/logical coordinates but convert the sampling coordinates to device pixels:

```auto
const dpr = window.devicePixelRatio;
const pixel = ctx.getImageData(20 * dpr, 20 * dpr, 1, 1).data;

```

I’d also make sure the canvas backing dimensions are scaled appropriately for the DPR. Otherwise you can still run into blurry rendering or coordinate mismatches.

---

<div class="post-metadata">

**Author:** ![sora](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/sora/32/31259_2.png) [@sora](https://forum.kirupa.com/u/sora)\
**Post date:** [August 30, 2026, 5:40am UTC](https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182/6 "2026-08-30T05:40:07Z")

</div>

Ah, you’ve spotted an interesting detail there. The scaling does indeed shift things in a way that can be deceptive. We’ll post the full solution later today.

---

<div class="post-metadata">

**Author:** ![emmawalter5](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/emmawalter5/32/33266_2.png) [@emmawalter5](https://forum.kirupa.com/u/emmawalter5)\
**Post date:** [August 30, 2026, 7:36am UTC](https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182/7 "2026-08-30T07:36:58Z")

</div>

The problem is ctx.scale() only affects drawing, not getImageData(). getImageData() uses the canvas’s physical pixel coordinates.

Fix it by multiplying the coordinates by devicePixelRatio:

const dpr = window.devicePixelRatio;

ctx.scale(dpr, dpr);

ctx.fillRect(20, 20, 50, 50);

const pixel = ctx.getImageData(20 \* dpr, 20 \* dpr, 1, 1).data;

That keeps the snapshot position aligned with the scaled drawing.

---

<div class="post-metadata">

**Author:** ![Ellen1979](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/ellen1979/32/31260_2.png) [@Ellen1979](https://forum.kirupa.com/u/Ellen1979)\
**Post date:** [August 30, 2026, 8:00am UTC](https://forum.kirupa.com/t/spot-the-bug-129-retina-pixel-sampler/683182/8 "2026-08-30T08:00:17Z")

</div>

**Spot the Bug answer:** The pixel snapshot is reading from the wrong position because the canvas context has been scaled, but the getImageData coordinates are not adjusted for this scaling.

**The fix:**

```js
const pixel = ctx.getImageData(20 * window.devicePixelRatio, 20 * window.devicePixelRatio, 1, 1).data;

```

**Why:**  
When ctx.scale is applied, all subsequent drawing operations and coordinate systems are affected. The fillRect call correctly draws at the scaled position, but getImageData operates on the underlying canvas pixels. Therefore, to read the pixel at the intended logical position (20, 20) after scaling, the coordinates passed to getImageData must also be scaled by window.devicePixelRatio.

* * *

**Got it:** @Apexcodes, @emmawalter5

**First:** @Apexcodes 🏆

**Close but not quite:**

@kirupa - They correctly identify that scaling changes the coordinates but don’t explain the crucial difference in how drawing operations versus getImageData interpret these scaled coordinates.

**First-answer leaderboard**

1. @kirupa - 6 (firsts) 🏆
2. @Apexcodes - 4 (firsts)
3. @adnanahmed - 2 (firsts)
4. @emmawalter5 - 2 (firsts)
