Have you ever thought that the sprites on Showdown looked a bit too blurry and that 100% zoom was too small, and found 200% zoom to be too big?
if that applies to you, then this is what you are looking for!
this simple UserScript makes the Pokémon sprites during battle use Nearest neighbor scaling!
Here's a few screenshots to compare (before and after):
(all of the screenshots are taken on a 1920x1080 display at 175% zoom)
here's the UserScript:
in order to use it you'll need either Violentmonkey or Tampermonkey installed on your browser.
enjoy!
(shamelessly made using ChatGPT)
v1.1: fixed the "pixelated" image-rendering sometimes getting overriden.
if that applies to you, then this is what you are looking for!
this simple UserScript makes the Pokémon sprites during battle use Nearest neighbor scaling!
Here's a few screenshots to compare (before and after):
(all of the screenshots are taken on a 1920x1080 display at 175% zoom)
here's the UserScript:
in order to use it you'll need either Violentmonkey or Tampermonkey installed on your browser.
Code:
// ==UserScript==
// @name Showdown pixelated render
// @description Applies Nearest neighbor scaling to Pokémon sprites
// @version 1.1
// @match *://play.pokemonshowdown.com/*
// @match *://replay.pokemonshowdown.com/*
// @match *://*.psim.us/*
// @match ps.*
// @author F-00
// @source https://www.smogon.com/forums/threads/nearest-neighbor-sprites-scaling.3772597/
// @icon https://play.pokemonshowdown.com/favicon.ico
// @run-at document-end
// ==/UserScript==
(() => {
const applyStyles = () => {
// Handle background-image sprites
document.querySelectorAll('[style*="sprites/"], [class*="sprite"]').forEach(el => {
const bg = getComputedStyle(el).backgroundImage;
if (!bg || bg === 'none') return;
if (bg.includes('/sprites/types/')) {
el.style.setProperty('image-rendering', 'auto', 'important');
} else if (bg.includes('/sprites/')) {
el.style.setProperty('image-rendering', 'crisp-edges', 'important');
}
});
// Handle <img> elements with sprite sources
document.querySelectorAll('img[src*="/sprites/"]').forEach(img => {
if (img.src.includes('/sprites/types/')) {
img.style.setProperty('image-rendering', 'auto', 'important');
} else {
img.style.setProperty('image-rendering', 'pixelated', 'important');
img.style.setProperty('-ms-interpolation-mode', 'nearest-neighbor', 'important'); // legacy support
}
});
};
// Initial application
applyStyles();
// Observe dynamic changes (Showdown replaces sprites frequently)
const observer = new MutationObserver(mutations => {
for (const m of mutations) {
for (const node of m.addedNodes) {
if (node.nodeType === 1) {
if (node.matches?.('img[src*="/sprites/"]')) {
if (node.src.includes('/sprites/types/')) {
node.style.setProperty('image-rendering', 'auto', 'important');
} else {
node.style.setProperty('image-rendering', 'pixelated', 'important');
node.style.setProperty('-ms-interpolation-mode', 'nearest-neighbor', 'important');
}
} else if (node.querySelectorAll) {
node.querySelectorAll('img[src*="/sprites/"]').forEach(img => {
if (img.src.includes('/sprites/types/')) {
img.style.setProperty('image-rendering', 'auto', 'important');
} else {
img.style.setProperty('image-rendering', 'pixelated', 'important');
img.style.setProperty('-ms-interpolation-mode', 'nearest-neighbor', 'important');
}
});
}
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
// Fallback interval in case observer misses updates
setInterval(applyStyles, 2000);
})();
(shamelessly made using ChatGPT)
v1.1: fixed the "pixelated" image-rendering sometimes getting overriden.
Last edited: