A fragment shader can make a complete image without loading a model, placing a light or importing a texture. Give it a position and a few parameters, and it returns a color. That small interface is part of the appeal: there is very little room to hide from the relationship between the equation and the picture.

I saved a Shader Toy extension for Visual Studio Code because the editing loop is so direct. Change the source, see the image change. The extension supplies familiar values such as the viewport resolution and elapsed time. The interesting work happens inside the function that interprets them.

Here is a minimal GLSL example using the extension's iGlobalTime convention:

void main() {
    vec2 p = (2.0 * gl_FragCoord.xy - iResolution.xy)
             / iResolution.y;
    vec2 center = vec2(0.3 * sin(iGlobalTime), 0.0);
    float d = length(p - center) - 0.25;
    float edge = 2.0 / iResolution.y;
    float circle = 1.0 - smoothstep(-edge, edge, d);
    vec3 ink = vec3(0.1, 0.7, 0.8);
    gl_FragColor = vec4(ink * circle, 1.0);
}

Every fragment runs the same code with a different gl_FragCoord. Subtracting the viewport size centers the coordinates. Dividing both axes by the viewport height gives them the same scale, so a circle stays circular in a wide window. Dividing each axis by its own dimension would instead stretch the coordinate system to fit the rectangle.

The distance expression is the whole shape. length(p - center) measures how far a fragment lies from the moving center. Subtracting the radius makes the value negative inside, zero on the boundary and positive outside. The color step then gives that distance a visible interpretation. A hard comparison would work, but a short transition around zero makes the edge less harsh.

Time moves the center along a sine wave. It doesn't advance a stored object from its previous position. The position is recalculated directly from the current time. Jump the clock forward, and the image still has a well-defined answer. That makes this particular animation easy to scrub or reproduce.

This style of programming encourages small experiments with large visual consequences. Replace one circle with the minimum of two distance fields and they form a union. Repeat the coordinate system before measuring distance and a single object becomes a grid. Distort the coordinates gradually and the same drawing function appears to bend.

The limit is equally useful to understand. A fragment usually has no convenient knowledge of what another fragment just calculated. A simulated fluid with persistent state needs more machinery than this example. A shader is a small world, but its rules include what information each point is allowed to see.