Technical Insights
Educational and historical documentation about how the system powering this site works
Pattern Transformations
Counter-Strike 2 and Counter Strike Global Offensive use a single base texture (like Case Hardened) to generate hundreds of unique-looking skin variants through a system of randomized transformations. This is achieved via a deterministic, seed-based mechanism β a key to understanding and reproducing Blue Gem patterns outside of the game.
Why is it called a Paint "Seed"?
To generate texture variety in a consistent yet randomized way, Valve employs a pseudo-random uniform generator built into the Source Engine. Since computers are deterministic machines, they require an initial value β a seed β to produce randomness. In CS2, each skin is assigned a paint seed ranging from 1 to 999. This seed initializes the random number generator, which then produces a specific set of values used to apply transformations to the texture.
Because this algorithm is deterministic and uniform across all platforms, a given paint seed will always yield the same transformation β ensuring that the skin looks identical on every system.
TL;DR: The paint seed initializes Valve's pseudo-random generator to define how the base texture is positioned, rotated, and scaled for pattern, wear, and grunge layers.
How are Texture Transformations Computed?
To understand how CS2 randomizes skin appearances, it helps to understand UV mapping: each weapon model has a UV layout that defines how a 2D texture wraps onto the 3D mesh. Valve applies the texture to this UV map and then uses the paint seed to randomize its translation, rotation, and scale.
This affects not just the main pattern (like the Case Hardened color blotches), but also the wear and grunge overlays β textures that add scratches, aging, and grime.
If a skin's style doesn't use random displacement (e.g., possibly solid color skins like Asiimov), it won't be affected by this system.
UV Mapping Concept
Hover over the cube faces or UV map to see how 3D surfaces map to 2D texture coordinates
3D Model
UV Map (Unfolded)
Step-by-Step: Paint Seed β Transformation
1. Determine Base Scale
Each weapon and paint style has a specific scale, usually defined in Valve's items_game.txt. The base scale is calculated from the weapon length and the UV scale, depending on the paint style:
if (paint_style == 3 || paint_style == 6) {
Β Β Β Β scale = weapon_length * 0.027777778;
} else {
Β Β Β Β scale = uv_scale;
}
For example, the Karambit has:
- WeaponLength = 9.813000
- UVScale = 0.438000
- β resulting in a base scale of 0.438
2. Generate Random Values
Once the base scale is known, the paint seed is used to generate 11 pseudo-random float values, which define how each texture layer is applied:
Texture Layer | Parameters |
---|---|
Pattern | scale, translateX, translateY, rotate |
Wear | scale Γ multiplier, translateX, translateY, rotate |
Grunge | scale Γ multiplier, translateX, translateY, rotate |
These floats are generated in fixed order and ranges:
Pattern
- translateX β 0.0 β 1.0
- translateY β 0.0 β 1.0
- rotate β 0.0 β 360.0
Wear
- scaleMult β 1.6 β 1.8
- translateX β 0.0 β 1.0
- translateY β 0.0 β 1.0
- rotate β 0.0 β 360.0
Grunge
- scaleMult β 1.6 β 1.8
- translateX β 0.0 β 1.0
- translateY β 0.0 β 1.0
- rotate β 0.0 β 360.0
These values are applied independently to each layer using the following logic:
Transform Order: Scale β Translate β Rotate
(Repeated for pattern, wear, and grunge)
Example: Karambit | Case Hardened β Paint Seed 633
Using the above logic, paint seed 633 on a Karambit yields:
- Base Scale: 0.438
- Translate X: 0.471 β shifts the pattern ~47% left
- Translate Y: 0.624 β shifts the pattern ~62% up
- Rotation: 96.7Β° counterclockwise
The wear and grunge layers get their own values, also derived from the seed but with different multipliers and angles.
Remaining Mystery: Rotation Center
For a long time, one question remained: where exactly is the rotation pivot? While <180Β° angles appeared to rotate around the UV's top-left corner, higher angles behaved inconsistently when trying to simulate the result outside the game.
From Reddit to pattern.wiki
The earliest attempt to reverse-engineer this system came from the famous Reddit post by Step7750 back in 2016. That post laid out the basic idea of how seed-based transformations work β and for years, it served as the de facto reference for the community.
Eventually, sites like Broskins and csfloat.com built working preview systems β but the actual logic used by Valve remained undocumented for the public.
That changed in early 2024, when pattern.wiki published a full breakdown of the transform process β including specific values, math, and visual examples. It was a major leap forward.
What We Clarified
While pattern.wiki's implementation clearly works in practice, their description of the transform order and rotation pivot didn't fully align with how Source behaves under the hood. Through testing and debugging, we clarified a few subtle (but important) points:
- All transforms are applied around the origin (0, 0) β not after translation
- The effective matrix order is: Tβ Γ R Γ S Γ Tβ
Template Transform Application (Finalized Model)
To sample the correct area of the texture using a paint seed, we apply a sequence of affine transformations β each one modifying the UV coordinates before sampling the base texture.
Step-by-Step Transform Chain
1. Centered Translation
Shift the pattern using the paint seed's offsets, but center the transform around the UV origin:
translate(offsetX - 0.5, offsetY - 0.5)
2. Scale (around origin)
Uniformly scale the pattern using the base scale β around the origin (0, 0):
scale(scaleX, scaleY)
3. Rotation (around origin)
Rotate the pattern counterclockwise from the origin:
rotate(rotation)
4. Extra Offset (final adjustment)
After rotation and scale, apply a final offset that compensates for the corner-based rotation:
invScale = 0.5 / scale
angle = -rotationDeg * (Ο / 180)
extraX = invScale * cos(angle) - invScale * sin(angle)
extraY = (extraX * sin(angle)) + (invScale * cos(angle))
translate(extraX, extraY)
Final Matrix Sequence
In affine terms, this creates a full transformation matrix:
A = Tβ Γ R Γ S Γ Tβ
Or in plain English:
- Center UV offset
- Apply scale
- Rotate around origin
- Correct with extra offset
The final transformed UVs are then wrapped using modulo:
uv_final = (A Γ uv_coords) % 1.0
In order to understand this approach better, you can play around with our texture transform studio that applies exactly these transformations live in your own browser.