Palette shader (lospec compatible)

This palette shader uses 1x palette textures to do it’s work. It does so by sampling from the 1x textures you can find on websites like lospec.

To make this shader work, use a Sprite (or other CanvasItem with pixels) or cover the area you want to change the colors of with a ColorRect. Then use this shader, set up the amount of colors in the palette texture and drag in the palette texture itself.

Why not try the world famous PICO-8 palette for your next jam game! Confuse everyone haha.

Shader code
shader_type canvas_item;

uniform sampler2D palette : hint_black; // Insert a palette from lospec for instance
uniform int palette_size = 16;

void fragment(){ 
	vec4 color = texture(SCREEN_TEXTURE, SCREEN_UV);
	vec4 new_color = vec4(.0);
	for (int i = 0; i < palette_size; i++) {
		vec4 palette_color = texture(palette, vec2(1.0 / float(palette_size) * float(i), .0));
		if (distance(palette_color, color) < distance(new_color, color)) {
			new_color = palette_color;
	COLOR = new_color;
Color, lospec, palette, pixel
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

Earthbound-like battle background shader w/scroll effect and palette cycling

Palette Shader

Extensible Color Palette


Newest Most Voted
Inline Feedbacks
View all comments
11 months ago

I did one exactly like yours Palette Filter For 3D and 2D – Godot Shaders

11 months ago

i think you missed a small offset (its not often visible but it can be):
when iterating over the points, you should use

vec4 palette_color = texture(palette, vec2(1.0 / float(palette_size) * (float(i) + 0.5), .0));

the “+0.5” takes care of “centering” in each pixel of the palette – otherwise you can hit edges between pixels.
Happened to me: i took a plalette with 36 colors but had to increase the palette_size to 37 for the sampling to work.
Reason: 35/36 = 0.97222… which is exactly where the pixel should start, but through floating point error, you get the 35th instead of the 36th color, therefor the last color never gets used.