hey2d avatar

Understanding fract() in GLSL: The Secret Behind Repeating Patterns

hey2d

Published: 04 Jul 2026 › Updated: 04 Jul 2026Understanding fract() in GLSL: The Secret Behind Repeating Patterns

Understanding fract() in GLSL: The Secret Behind Repeating Patterns

Before continuing with this lesson, if you are joining this series for the first time, I highly recommend reading the complete overview of everything we have covered so far.

A Beginner's Journey Through GLSL So Far
@hey2d/a-beginners-journey-through-glsl-so-far-d2z

That article covers the foundations we have been building throughout this series, including:

  • Drawing a solid color on the screen
  • Understanding UV coordinates
  • Horizontal and vertical gradients
  • The mix() function
  • The clamp() function
  • Several small exercises that help build intuition instead of memorizing code

Once you are comfortable with those ideas, this lesson will feel like a natural next step.


Why Do Shader Patterns Repeat?

One of the biggest strengths of GLSL is that it can generate textures and patterns without using any images. Instead of reading pixels from a file, we create everything with mathematics.

If you have ever wondered how shaders produce checkerboards, brick walls, tiles, circuit boards, or countless repeating shapes, the answer often begins with one simple function.

fract()

At first, fract() seems almost too simple to be useful. It only returns the decimal part of a number. Yet this tiny function is responsible for many of the repeating patterns you see in procedural graphics.

In this lesson, we are not building a complete grid yet. Instead, we are learning the first building block that makes those grids possible.


What Does fract() Actually Do?

The word fract comes from fractional part.

Whenever you give it a number, it removes the whole number and keeps only what comes after the decimal point.

For example,

fract(0.35)

returns

0.35

because there is no whole number to remove.

Now try

fract(4.35)

The whole number is discarded.

The result becomes

0.35

It does not matter how large the number becomes.

fract(9.72)

returns

0.72

and

fract(103.15)

returns

0.15

The integer disappears, leaving only the fractional part.


Visualizing the Pattern

Imagine counting from zero upward.

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0
1.1
1.2
1.3

Without fract(), the numbers simply continue increasing forever.

Now imagine applying fract() to every value.

0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
0.0
0.1
0.2
0.3

Every time the value reaches a whole number, it starts over.

Instead of climbing forever, the numbers continuously loop between 0 and 1.

That looping behavior is exactly what makes repeating textures possible.


Using fract() with UV Coordinates

Throughout this series we have been using vUv.

Remember that UV coordinates already move across the screen from 0 to 1.

Left ---------------------- Right

0.0                     1.0

If we simply write

vec3 color = vec3(vUv.x);

we get one smooth horizontal gradient.

Nothing repeats because the UV coordinates only travel across the screen once.

To make repetition possible, we first stretch the coordinates.

float density = 10.0;

vec2 g = fract(vUv * density);

Do not worry too much about density yet.

For now, simply notice that fract() is being applied to the UV coordinates.

Instead of seeing one journey from 0 to 1, the coordinates keep restarting.

Every restart creates another tile.


Why Doesn't fract(vUv) Change Anything?

A common question is why we do not simply write

vec2 g = fract(vUv);

The answer is that vUv already stays between 0 and 1.

There is no whole number for fract() to remove.

That means

fract(0.25)

is still

0.25

and

fract(0.90)

is still

0.90

Nothing changes.

The interesting behavior only appears after the values become larger than 1.

That is why we multiply the coordinates before applying fract(). We will look at that multiplication in detail in the next lesson.


Seeing Repetition in Action

Imagine the x coordinate after multiplication.

Instead of moving like this

0  1

it now behaves like this

0
0.2
0.4
0.6
0.8
1.0
1.2
1.4
1.6
1.8
2.0

Applying fract() changes those values into

0.0
0.2
0.4
0.6
0.8
0.0
0.2
0.4
0.6
0.8
0.0

The coordinate keeps looping.

Each loop becomes another repeated section of the image.


Exercise

Use the following shader.

#ifdef GL_ES
precision mediump float;
#endif

varying vec2 vUv;

void main()
{
    float density = 5.0;

    vec2 g = fract(vUv * density);

    vec3 color = vec3(g.x);

    gl_FragColor = vec4(color, 1.0);
}

Run the shader exactly as it is.

You should see the same horizontal gradient repeated several times across the screen.

Now change only one number.

float density = 2.0;

Notice that there are fewer repetitions.

Next try

float density = 10.0;

The repeated sections become much smaller because more copies fit across the screen.

Finally, experiment with

float density = 20.0;

The repetitions become even more frequent.

Nothing about the gradient itself has changed.

The only thing that changes is how often it restarts.


Breaking Down the Exercise

The important line is

vec2 g = fract(vUv * density);

The multiplication increases the UV values beyond their normal range.

The fract() function immediately removes the whole number, keeping only the decimal portion.

Because the decimal portion always falls between 0 and 1, the coordinates continuously restart.

That restarting behavior is what creates repetition.

Although we are only displaying a grayscale gradient, the exact same idea can later be used to repeat colors, shapes, circles, textures, and much more.


Why This Function Matters

It is easy to think of fract() as a small utility function, but it appears in countless procedural shaders.

Whenever you see a repeating pattern, there is a good chance fract() is involved somewhere.

It allows one piece of logic to be reused over and over again without writing the same code multiple times.

Instead of drawing one square, you can draw hundreds.

Instead of creating one stripe, you can create an entire wallpaper.

Instead of building one tile, you can cover the entire screen.

All because the coordinates never stop restarting.


Leave Understanding fract() in GLSL: The Secret Behind Repeating Patterns to:

Written by

nothing..

Read more #learning posts


Best Posts From hey2d

We have not curated any of hey2d's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.

More Posts From hey2d