Skip to content

Shaders Explanation🔗

A Little Introduction

In today's world, most of us have screens with resolutions of at least 1920×1080 and higher. However, back in the days of our favorite retro games, this was far from the case. Older TVs operated at around 720×525 resolution, and handheld devices like Game Boys had even lower resolutions.

While we enjoy our collections of retro games today, there's one big problem: our modern screens usually have resolutions far beyond what these games were originally designed for. In practice, this means we either play our games in a tiny square in the middle of our screens to preserve their original resolution, or (what most of us prefer) we scale the image up to better fit our modern, high-resolution displays.

There are really three main scaling modes you can choose from in the frontend options under Screen Scaling:

  • Native – This keeps the game at its original resolution, resulting in that tiny box in the center of your screen.
  • Aspect – This scales the image to your screen while maintaining the original aspect ratio, so nothing looks distorted (no "fat" Super Mario). It scales the image up until either the width or height reaches the screen size. This often leaves empty space on the sides or top and bottom, unless you're lucky enough to have a screen that matches the original aspect ratio exactly.
  • Full Screen Stretch – This simply stretches the image to fill the entire screen without preserving the aspect ratio. While it fills the screen nicely, it can make things look strange if the original and screen aspect ratios don't match.

Ultimately, the choice is yours based on what you prefer. However, understanding screen scaling is important because it plays a big role in how shaders are used — although scaling isn't the only reason shaders are popular, it is a major one.

Lets get shady!🔗

Options → Frontend → Screen Sharpness

First, let’s talk a little about this option.
The GPU in your device basically has two modes to upscale an image to the final screen size.

The first is NEAREST, which is faster because it’s less complex. It simply upscales the image by doubling each pixel. This looks very sharp, but it can make an image look too sharp, especially if the input (the output of your emulator core) is at a very small resolution. NEAREST just doubles the pixels until it reaches the desired size.

The second option is LINEAR. This costs a little more performance-wise, but instead of just doubling pixels, it applies linear interpolation between pixels, creating a smoother, softer look.
This can definitely create a more even, pleasant image — but again, it depends heavily on what the original source image looks like. If your emulator is outputting a very small image, LINEAR interpolation has to invent a lot of new pixels to fill the screen, and the result can look very blurry.

NEAREST LINEAR

The takeaway here is simple:
The smaller the original image the GPU receives to upscale to your screen’s resolution, the worse it will look.
Neither NEAREST nor LINEAR can magically make a tiny 320×240 image look amazing on a big 1024×768 screen.
That’s just been a fact of life since the early days of computers.


Now, if you really just want to rawdog the tiny emulator output directly to the screen for maximum performance, I recommend using NEAREST.
LINEAR usually just ends up looking too blurry, while NEAREST faithfully replicates the pixels from the source, simply making the image bigger — simple and sharp.


So, does that mean old games will always look like crap on modern screens?

Well, no, not really! Welcome to the world of shaders!


A Note on the Word "Shader"🔗

Before we dive in, a quick note:
The word shader is actually kind of wrong in this context. In emulation, "shaders" have become synonymous with "effects," but technically speaking, shaders are just small programs that tell your GPU what to do with pixel data.
Every image you see on your screen — even a simple one without effects — is being drawn by a shader.
Even the most basic output still runs a shader that simply says: "draw a rectangle the size of the screen and fill it with the emulator's pixel data."

Okay, with that out of the way — since we already use shaders to draw the screen, we can also use them to alter the look of the image!


Making LINEAR Look Good🔗

Let's start with a very basic idea that already looks great for most systems and might even be all you need:

Remember how I said NEAREST is better because LINEAR gets too blurry?
Well, LINEAR can actually be awesome — if we prepare the image a little first.

Here’s the problem:
When you scale a small image directly up to a big resolution with LINEAR interpolation, the GPU has to invent a lot of made-up pixels, which makes everything blurry.
(For example, scaling a 320×240 image up to 1024×768 — almost two-thirds of the final pixels are imaginary! No wonder it looks so bad.)


So what if we first scale the image sharply with NEAREST — and then apply LINEAR interpolation afterward?

In other words:
First, double (or triple) the sharp pixels with NEAREST, then let LINEAR smooth out only the small leftover gaps.
Best of both worlds!

Example: - Emulator outputs 320×240.
- First, upscale to 640×480 using NEAREST. (Or even 960×720 if you want.) - Then, apply LINEAR interpolation when stretching it the rest of the way to your screen’s full resolution.

Result?
It looks way better!
Because LINEAR now only has to invent half as many pixels as it’s working with a bigger starting point.

LINEAR with 2x NEAREST prescale


How to Set This Up🔗

First, let’s set a shader to upscale the image 2× using NEAREST:

Go to:

Options → Shaders

And set the following:
- Number of shaders: 1 (we only need one)
- Shader 1: stock.glsl (basic shader that just outputs the input image)
- Filter: NEAREST (very important — we want a sharp, clean NEAREST upscale first!)
- Source type: Relative or Source (doesn’t matter — they’re the same for the first shader)
- Texture type: Relative or Source
- Scale: 2 or 3
(Use 2× for SNES, MD, etc., or 3× for very small images like GB/GBC.)


What this shader does:
It takes the emulator's output, scales it up 2× or 3× using NEAREST, and passes it on to the next stage (in this case, directly to the screen, since we’re only using one shader).


Now, let’s apply the final LINEAR smoothing when outputting to the screen:

Go to:

Options → Frontend → Screen Sharpness

And set this to LINEAR.


So now: - First, the shader upscales the image 2× or 3× sharply with NEAREST. - Then, the frontend upscales the rest of the way using LINEAR to smooth it out slightly.

And the result already looks MUCH better, right?

First one is without shaders just straight NEAREST upscaling to fullscreen, second one is using above settings making things a little more smoother and nicer, this looks really good on the Brick's high dpi screen. Super Mario World 2025-04-26-16-27-31 Super Mario World 2025-04-26-16-27-21


A Few Final Notes🔗

The "world of shaders" is all about chaining small steps together — each shader alters the image slightly before passing it along.

  • Choosing NEAREST or LINEAR only matters when the step involves resizing.
    If a shader step uses a Scale of 1 (no scaling), the NEAREST or LINEAR setting has no effect at all. The same goes for the screen sharpness setting in frontend options. If an image already is at screen size before it reaches this final step then the final filter does nothing because it doesn't have to up or downscale anymore. NEAREST and LINEAR only apply when the image has to resize either up or down. Its not a filter applying to the image, it just tells the GPU what method to use when up (or down) scaling an image.

But like I said shaders are not only used for scaling, since they are just little programs that run on your GPU they can do anything. There are tons of shaders out there (.glsl files) that do all kinds of fun things:
- Simulate CRT screens
- Mimic Game Boy Advance LCD looks
- Apply different smoothing/sharpening algorithms
- Add scanlines, fake grid patterns
- Distort the screen or enhance colors, etc.

Feel free to experiment!
Try different shaders, different combinations, and find the look that feels best for your games. You can download any glsl file and place it in the /Shaders folder on your SDCard to use it within NextUI. But remember it has to be the glsl file which is the actual shader program, we currently do not yet support the glsp shader preset files. But these are simple text files and you can open them to view the settings and replicate them in the shaders menu.

Enjoy! 🎮

BONUS: GB/GBC/GBA Tip!🔗

Want to make your Game Boy, Game Boy Color, and Game Boy Advance games look even cooler? Here's a quick bonus trick!

Set everything up the same way as above, but now set the number of shaders to 2 and configure the second shader like this:

  • Number of shaders: 2
  • Shader 2: lcd3x.glsl (a shader that overlays a pixel grid, simulating old handheld screens)
  • Filter: NEAREST (NEAREST recommended for a sharp grid, but LINEAR could work more for you)
  • Source type: Source (important — I’ll explain why below)
  • Texture type: Source
  • Scale: Screen

What This Does🔗

This setup applies an additional shader called lcd3x, which overlays a grid on the image to mimic the look of original Game Boy, Game Boy Color, and GBA screens.
It’s a simple effect, but it adds a lot of nostalgic charm!

lcd3x


Why Use Source and Screen?🔗

Here's the idea:

  • Setting Source as the Source/Texture Types tells the shader to use the original dimensions from the emulator output — the real, original pixel layout. However, for the actual pixel data, it still uses the output from the previous shader step.
  • While we're at it: Relative means "use the dimensions of the previous shader step," and Screen means "use the dimensions of the screen" (with aspect ratio correction applied).
  • Setting the Scale to Screen means the shader will output an image with a grid drawn over it (where the grid itself is based on the original pixel size) at the screen’s resolution. The upscaling happens here, and no further upscaling is needed afterwards since the output is already at the correct size by this shader by setting it to screen.

So, the lcd3x shader creates a grid based on the original pixel size but with an output size that covers the entire screen.
You end up with an LCD-style grid that perfectly matches the original pixel layout, giving each pixel a distinct look — while in reality, the image is being upscaled to your screen’s full resolution.
It looks like your Game Boy screen suddenly has four times as many pixels. If that ain't cool I don't know what is!


Important Note About Filtering🔗

Since Shader 2 is already stretching the image up to full screen, the frontend's Screen Sharpness setting no longer matters.
At this point, the image is already at its final size before it even reaches the frontend’s final filter.

That means:

The final image sharpness is controlled inside Shader 2 itself, not by the frontend option anymore.


That's it — now you’ve got a sharp, beautiful pixel grid overlay just like an old-school Game Boy screen! 🎮🖤