Why is my circle an oval? UV coordinates and aspect ratio in shaders

Updated 2026-10-12

Almost everyone's first shader circle comes out as an oval. Nothing is wrong with the circle code. The problem is the coordinates it was given, and once you see it you will spot the same mistake in other people's shaders too.

What UV actually is

A fragment shader is handed a position for each pixel, usually called uv. It runs from (0, 0) at the bottom left to (1, 1) at the top right. It is a fraction of the picture, not a count of pixels. Halfway across is 0.5 whether the picture is 100 pixels wide or 4,000.

That is exactly why UV is so useful: the same shader works at any resolution. But it hides a catch. Suppose the picture is 400 pixels wide and 300 tall:

  • A step of 0.1 in uv.x is 40 pixels sideways.
  • A step of 0.1 in uv.y is only 30 pixels upwards.

So one unit of UV is a different physical length on each axis. Ask for "everything within 0.25 of the middle" and you get an area that is 200 pixels wide and 150 tall: an ellipse.

The naive circle

float4 frag(float2 uv)
{
    float d = length(uv - 0.5);          // distance from the middle
    float inside = step(d, 0.25);        // 1 inside, 0 outside
    return float4(inside, inside, inside, 1.0);
}

On a 4:3 picture this draws a white oval. Subtracting 0.5 correctly moves the origin to the middle, but the two axes still disagree about how long a unit is.

The fix: scale x by the aspect ratio

The aspect ratio is width divided by height: 400 / 300 = 1.333. If we stretch the x coordinate by that amount, one unit means the same number of pixels in both directions:

float4 frag(float2 uv)
{
    float2 p = (uv - 0.5) * float2(aspect, 1.0);   // centered, and equal units on both axes
    float d = length(p);
    float inside = step(d, 0.25);
    return float4(inside, inside, inside, 1.0);
}

Now p.y runs from -0.5 to 0.5 and p.x runs from -0.667 to 0.667 (on a 4:3 picture). A radius of 0.25 is the same length in every direction, so you get a circle. In this site's lessons aspect is provided for you. In Unity you would compute it from the screen size, and in Shadertoy from iResolution.

Order matters: center first, then scale

A very common slip is to scale first and subtract afterwards:

float2 wrong = uv * float2(aspect, 1.0) - 0.5;      // the middle is no longer the middle
float2 right = (uv - 0.5) * float2(aspect, 1.0);     // the middle stays at (0, 0)

After uv * aspect, the horizontal middle sits at 0.667, not 0.5, so subtracting 0.5 leaves your shape drifting off to one side. Centering first and scaling second keeps the origin in the middle, which is where every symmetric shape (circles, rings, rotations) wants it.

When you should not correct for aspect

Aspect correction is for shapes that must keep their proportions: circles, squares, rotations. For things that should stretch with the picture it is wrong. A sky gradient, a vignette that follows the edges, or a background that fills the frame can use plain uv. A good habit is to keep both: uv for "where in the picture am I", and p for "where am I in a space with equal units".

A second way: work in pixels

Some people divide by the height only. With pixel coordinates fragCoord and resolution res:

float2 p = (fragCoord - 0.5 * res) / res.y;

This gives the same result as the version above: p.y runs from -0.5 to 0.5, and p.x is automatically wider by the aspect ratio. Shadertoy shaders use this form so often that it is worth recognising.

Check which way is up

In these lessons uv.y grows upwards. Some engines and image formats put the origin at the top-left instead, so your picture can appear upside down when you move a shader between tools. If a shape is mirrored vertically, try uv.y = 1.0 - uv.y before anything else. A quick test is to output float4(uv.y, 0, 0, 1): the red end tells you which edge is "top".

Quick checklist

  • Circle comes out as an oval: multiply the x coordinate by the aspect ratio.
  • Shape is off to one side: you scaled before you centered.
  • Background looks wrong after correcting: it should probably use plain uv.
  • Picture is upside down: flip uv.y.

Try it yourself

The lesson UV: where am I? shows the coordinates as colors so you can see them. Center, distance and aspect ratio walks through exactly the oval-to-circle fix above, and Distance fields and antialiasing shows how to make the edge of that circle smooth.

Keep reading

Start the interactive lessons