How to squash/stretch individual tiles in a TileMapLayer via Shader?

Godot Version

4.6

The Goal

I’m trying to make tiles (houses) in a TileMapLayer squash and stretch in time with music.Like this:

Screencast from 02-16-2026 09-13-12 PM(This are Sprite2D)

I have this working perfectly with Sprite2D nodes, but when I apply the same logic to a TileMapLayer, the tiles aren’t at the right position.

The Working Sprite2D Setup

Currently, I use a RhythmNotifier to trigger a Tween that updates a deformation uniform in this shader:

// Sprite2D Shader (Works)
shader_type canvas_item;
uniform vec2 deformation = vec2(0, 0);

void vertex() {
    vec2 scale = 1.0 - abs(deformation);
    VERTEX.x *= (scale.x / scale.y);
    VERTEX.y *= (scale.y / scale.x);
}

The Problem with TileMapLayer

When applied to a TileMapLayer, scaling the VERTEX moves the tiles relative to the map origin.

I tried to “re-center” the scaling by subtracting the tile origin before the scale and adding it back afterward, but this breaks the squash effect. Instead of scaling the individual tiles in place, it just shifts their entire positions based on the deformation value.

My Attempted Shader Fix:

shader_type canvas_item;
uniform vec2 deformation = vec2(0, 0);
uniform vec2 tileSize = vec2(32.0, 32.0);

void vertex() {
    // Attempting to find the top-left of the tile
    vec2 tile_origin = floor(VERTEX / tileSize) * tileSize;
    vec2 scale = 1.0 - abs(deformation);

    // Subtract origin to scale locally, then add back
    VERTEX -= tile_origin;
    VERTEX.x *= (scale.x / scale.y);
    VERTEX.y *= (scale.y / scale.x);
    VERTEX += tile_origin;
}

Questions:

  1. My tile_origin doesn’t give back the right position. Is there a built-in way to get the tile’s center/origin in the vertex shader or a better way to do it?

  2. If I get the tile_origin correctly, is this “Subtract → Scale → Add back” approach valid for a TileMap, or do I have a logic error in my code?

I do not use Godot but I would assume quads built by tilemaps have their origin at top right and not center of a tile.

The problem will be multiplication as that uses the absolute coordinates.

If you know which vertex you are operating on you could handle each vertex individually, but I don’t know if Godot has something corner ids.

This post looks interesting

They use VERTEX_ID, sounds like that could be used in your case.

1 Like

Thank you that was really the solution.

This is the Shader if anyone is interested

Screencast from 03-03-2026 09_28_04 PM

shader_type canvas_item;

uniform vec2 deformation = vec2(0, 0);

uniform vec2 tileSize = vec2(32.0, 32.0);
void vertex() {
vec2 world_pos;
vec2 pointInTileMap;
world_pos = (MODEL_MATRIX vec4(VERTEX,0.0,1.0)).xy;
float xValue;
float yValue;
if (VERTEX_ID == 0)
{
xValue = - tileSize.x/2.0;
yValue =  + tileSize.y/2.0;
}
else if (VERTEX_ID == 1)
{
xValue = - tileSize.x/2.0;
yValue = - tileSize.y/2.0;
}
else if (VERTEX_ID == 2)
{
xValue =  + tileSize.x/2.0;
yValue =  - tileSize.y/2.0;
}
else if (VERTEX_ID == 3)
{
xValue =  + tileSize.x/2.0;
yValue =  + tileSize.y/2.0;
}
VERTEX.x += (xValuedeformation.x);
VERTEX.y += (yValue*deformation.y);

}

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.