I bought a used monitor a while back and did what everyone tells you to do first: pull up an online "dead pixel test" and cycle through some solid colors before deciding whether to keep it. It worked, but afterward I was curious why every single one of these tools looks identical — same handful of colors, same full-bleed look, no ads-covered corners, nothing fancy. So I built my own version to see what's actually involved, and it turns out almost none of the interesting work is in the colors. It's in getting a <div> to actually, reliably, cover the entire physical screen.
The colors are chosen, not decorative
The color grid isn't nine random swatches — each one is picked to isolate a specific kind of pixel failure:
const testModes = [
{ key: "red", style: { background: "#FF0000" } },
{ key: "green", style: { background: "#00FF00" } },
{ key: "blue", style: { background: "#0000FF" } },
{ key: "black", style: { background: "#000000" } },
{ key: "white", style: { background: "#FFFFFF" }, darkText: true },
{ key: "gray", style: { background: "#808080" } },
{ key: "yellow", style: { background: "#FFFF00" }, darkText: true },
{ key: "cyan", style: { background: "#00FFFF" }, darkText: true },
{ key: "magenta", style: { background: "#FF00FF" } },
];
Pure red, green, and blue each light up exactly one of a pixel's three sub-pixels — that's how you catch a sub-pixel that's stuck off (it stays dark against a color that should light it) or stuck permanently on in one channel (it shows up as a stray colored dot against black). Black and white check the opposite extremes: a "dead" pixel is usually defined as one that never lights at all, so a pure black fill is actually the easiest way to spot one — any point of light against total black is a defect, not a judgment call. Gray is there because it's R, G, and B mixed in equal parts; if the panel has any color tint or backlight unevenness, gray is where a human eye notices it fastest, since a slight tint is invisible against a color but obvious against neutral gray. Yellow, cyan, and magenta are two channels at once — useful for catching a sub-pixel that's stuck in a way pure R/G/B alone wouldn't reveal.
Then there are two patterns instead of flat colors:
{
key: "gradient",
style: { background: "linear-gradient(to right, #000000, #FFFFFF)" },
isPattern: true,
},
{
key: "checkerboard",
style: {
backgroundColor: "#fff",
backgroundImage:
"linear-gradient(45deg, #000 25%, transparent 25%, transparent 75%, #000 75%, #000), linear-gradient(45deg, #000 25%, transparent 25%, transparent 75%, #000 75%, #000)",
backgroundSize: "20px 20px",
backgroundPosition: "0 0, 10px 10px",
},
isPattern: true,
},
The gradient isn't for pixel defects at all — it's for banding. A cheap panel (or a panel driven at a reduced color depth) can't render a smooth 0–255 transition, so instead of a clean fade you see visible steps. The checkerboard does something similar for contrast and sharpness: alternating black/white squares make it obvious if edges look soft or if there's visible bleed between adjacent pixels, neither of which a flat color would show you.
Fullscreen isn't cosmetic — it's the whole point, and it's annoyingly stateful
The naive version of this tool is "put a colored div on the page." The actual requirement is that the color has to cover every pixel, including the space normally eaten by the browser's tab bar, address bar, and OS taskbar — otherwise you're not testing the whole panel. That means the Fullscreen API, not just big CSS:
const enterFullscreen = async (index = 0) => {
currentIndex.value = index;
const elem = fullscreenContainer.value;
try {
if (elem.requestFullscreen) {
await elem.requestFullscreen();
} else if (elem.webkitRequestFullscreen) {
await elem.webkitRequestFullscreen();
} else if (elem.msRequestFullscreen) {
await elem.msRequestFullscreen();
}
isFullscreen.value = true;
displayHint();
} catch (err) {
message.error(t("screenTest.fullscreenError"));
}
};
Two things about this bit me before I wired it up correctly. First, requestFullscreen() returns a promise that rejects if it's not called as a direct result of a user gesture — you can't call it inside a setTimeout or on page load and expect it to work, which is exactly why the tool requires a click on a color swatch rather than trying to auto-launch. Second, there's no unprefixed-only world yet: Safari still wants webkitRequestFullscreen, and old Edge/IE wanted msRequestFullscreen, so the fallback chain isn't legacy cruft, it's still load-bearing.
The other half of the problem is knowing when you've left fullscreen — the user can hit Escape at any point, and the app needs to notice and reset its own state, not just rely on the browser's native exit animation:
const handleFullscreenChange = () => {
if (
!document.fullscreenElement &&
!document.webkitIsFullScreen &&
!document.mozFullScreen &&
!document.msFullscreenElement
) {
isFullscreen.value = false;
stopAutoPlay();
}
};
document.addEventListener("fullscreenchange", handleFullscreenChange);
document.addEventListener("webkitfullscreenchange", handleFullscreenChange);
Without that listener, pressing Escape exits the OS-level fullscreen but leaves the app thinking it's still showing the color tester, so the next click does something confusing. The fixed-position overlay underneath (position: fixed; width: 100vw; height: 100vh; z-index: 99999) is really just a safety net for browsers where the fullscreen request silently fails — it covers the viewport even without real fullscreen, just not the parts of the screen the browser chrome still owns.
Auto-play is a plain interval, not a rendering-timed loop
The "auto play" button just steps through the color list on a timer:
const startAutoPlay = () => {
isAutoPlaying.value = true;
enterFullscreen(0);
autoPlayTimer = setInterval(() => {
nextColor();
}, 3000);
};
I want to flag what this isn't, because it's tempting to assume any color-cycling tool is secretly doing frame-rate or response-time measurement with requestAnimationFrame. It's not — there's no timing measurement here at all, just "switch to the next color every 3 seconds" so you can stand back and watch the whole panel without touching anything. setInterval is fine for that because nobody's timing precision matters at a 3-second cadence; it would be the wrong tool if you were trying to measure actual pixel response time or refresh-rate behavior, which needs frame-accurate deltas, not a coarse timer that browsers are free to throttle when a tab is backgrounded.
What this can't actually tell you
A couple of honest gaps, since a full-screen color page will always be a cheap proxy for real display measurement, not a replacement for it:
-
The colors you see aren't calibrated.
#FF0000is just a CSS value — what actually hits your eyes depends on the OS color profile, the monitor's own color space, whether a blue-light filter or "night mode" is active, and how the GPU compositor happens to render it. Two perfectly healthy panels can render "pure red" slightly differently; this tool can't and doesn't try to correct for that. - It only catches what a human eye can see in real time. A pixel that's stuck a very slightly wrong shade, viewing-angle color shift on cheaper IPS/VA panels, or backlight bleed that only shows up at specific brightness levels are all things a careful look under the right lighting conditions will catch far better than a quick scroll through nine colors.
-
Fullscreen support is inconsistent across mobile browsers. Some mobile Safari versions restrict or ignore
requestFullscreen()on arbitrary elements entirely, so on iOS you may just get the fixed-overlay fallback rather than true fullscreen, meaning the OS status bar area doesn't actually get tested.
None of that makes it useless — it's still the fastest way to catch the obvious stuff (a fully dead pixel, an obviously stuck one, ugly gradient banding) before you decide whether a monitor is worth returning. I turned the version I built into a small free tool: Screen Test Tool. No install, no sign-up, just click a color and go fullscreen.
Available in other languages
- 螢幕測試工具 — 繁體中文
- 屏幕测试工具 — 简体中文
- Screen Test Tool — English
- 画面テストツール — 日本語
- 화면 테스트 도구 — 한국어
- Outil de Test d'Écran — Français
- Инструмент Тестирования Экрана — Русский
- Bildschirmtest-Tool — Deutsch
- Alat Tes Layar — Bahasa Indonesia
- Herramienta de Prueba de Pantalla — Español
- Công Cụ Kiểm Tra Màn Hình — Tiếng Việt
- เครื่องมือทดสอบหน้าจอ — ไทย
- Narzędzie do Testowania Ekranu — Polski
- Ekran Test Aracı — Türkçe
- Strumento di Test dello Schermo — Italiano
- Ferramenta de Teste de Tela — Português
- Schermtesttool — Nederlands
- Інструмент Тестування Екрану — Українська
Top comments (0)