Getting “giggly” effect when slowly moving a sprite

I cooked up a quick animation to demonstrate what’s happening here:

Animation demonstrating source of ripple

The grid represents the output pixels of your display. I’ve overlaid on top of it the sliding sprite we want to sample, if we could render it with unlimited sub-pixel resolution.

The dots in the center of each grid cell represent their sampling point. Because we’re using Nearest-Nieghbour/Point filtering, that’s the only point in the texture they pay attention to. When the edge of a new colour crosses that sampling point, the whole pixel changes colour at once.

The trouble arises when the source texel grid doesn’t line up with our output pixels. In the example above, the sprite is 16×16 texels, but I’ve scaled it to occupy 17×17 pixels on the display. That means, somewhere in every frame, some texels must get repeated. Where this happens changes as we move the sprite around.

Because each texel is rendered slightly larger than a pixel, there’s a moment where it completely bridges the sampling points of two adjacent pixels. Both sampling points land within the same enlarged texel, so both pixels see that texel as the nearest one to sample from, and the texel gets output to the screen in two places.

In this case, since there’s only a 1/16th scale difference, each texel is only in this weird situation for a frame or two, then it shifts to its neighbour, creating a ripple of doubled pixels that appears to slide across the image.

(One could view this as a type of moiré pattern resulting from the interaction of the texel grid and the sampling grid when they’re dissimilar)

The fix is to ensure that you scale your pixel art so each texel is displayed at the size of an integer multiple of pixels.

Either 1:1

Animation showing ripple-free rendering at 1:1 scale

Or 2:1, 3:1…

Animation showing ripple-free rendering at 3:1 scale

Using a higher multiple lets the sprite move in increments shorter than its own texel size, without localized stretching that impacts the intended appearance of the art.

So: pay close attention to the resolution of your output and the scaling applied to your assets, to ensure you keep an integer multiple relationship between them. The blog post that CAD97 links has practical steps you can take to achieve this.

Edit: To demonstrate this in the Unity project you’ve uploaded, I modified the camera settings to match your pixels to units setting, and laid out the following test. The Mario at the top has a slightly non-integer texel-to-pixel ratio (1.01:1), while the Mario at the bottom has 1:1. You can see only the top Mario exhibits rippling artifacts:

Two Marios, one exhibiting artifacts

Leave a Comment

tech