CS2 Crosshair Share Codes: What Those 25 Characters Hold
Twenty-five characters hold eighteen bytes, twenty-one console commands, and forty-one bits that carry nothing at all. Every field, decoded and measured.
Somebody drops a code in chat. CSGO-Cn37R-YE7vo-pLCAL-aURmZ-z6zkG. You paste it into the crosshair menu and you have their crosshair, and that is normally the end of the interaction.
Underneath, those 25 characters are a base-57 number holding 18 bytes, and those bytes expand to 21 console variables. What follows is each one of them, read out of the codec rather than out of a settings menu.
Just want the commands? Paste the code into the CS2 Crosshair Code Decoder and it prints the 21
cl_crosshair*lines, ready for the console.
Why the code is exactly 25 characters
The alphabet has 57 characters in it, not 58 or 62. It is the alphanumerics minus 0, 1, I, l and g: the glyphs that swap places when somebody reads a code off a stream overlay instead of copying it.2
That gives each character log2(57), about 5.83 bits. Twenty-five of them come to 145.8 bits. The payload is 18 bytes, or 144.
So the code is 25 characters long because 24 would not fit. Twenty-four carry 140.0 bits, four short of the payload, and the twenty-fifth buys 1.8 bits of headroom that nothing uses. The container is very nearly full.
Which is not the same as saying it is used.
The eighteen bytes, one at a time
Strip the CSGO and the dashes, reverse the remaining 25 characters, read them as a base-57 big-endian number, and render that as 36 hex digits. Our example comes out as:
69 01 f3 04 af 51 d5 89 06 1e 6d 54 0c f4 2e 00 00 00
Big-endian, checksum first. The three trailing zeros are padding; 2e is the last byte that means anything.
Byte 0 is the checksum: the sum of the other seventeen, mod 256. Bytes 2 through 14 are the settings, several of them bit-packed two to a byte. Byte 2 is cl_crosshairgap as a signed value divided by ten, byte 14 is cl_crosshairsize as an unsigned one, byte 10 packs the colour index, the outline toggle and the inner split alpha into one octet.
Decoded, our example is somebody's purple crosshair:
| ConVar | Value |
|---|---|
cl_crosshairstyle | 2 |
cl_crosshairsize | 4.6 |
cl_crosshairthickness | 1.2 |
cl_crosshairgap | -1.3 |
cl_crosshaircolor | 5 (custom) |
cl_crosshaircolor_r/g/b | 175, 81, 213 |
cl_crosshairalpha | 137 |
cl_crosshair_drawoutline | 1, thickness 2 |
cl_crosshairdot | 1 |
cl_crosshair_t | 1 |
Re-encode that structure and you get the original string back, character for character. A clean round trip is what lets a tool decode a code, change one field and hand back something the game will still accept.
Forty-one of the 144 bits carry nothing
Set byte 1 to 0, or 99, or 255. The crosshair decodes identically every time, because nothing reads it. The encoder writes 1 there and the decoder skips straight past it, which is what a version byte looks like in a format that has never had a second version.
Bytes 15, 16 and 17 are the same story with less mystery: always written as zero, never read, three bytes of a big number that exist because 15 bytes of payload do not divide neatly into anything. And inside byte 13, the low bit is dead too. Style lives in bits 1 through 3, the four toggles live in the high nibble, and bit 0 sits between them doing nothing.
Add it up. Eight bits of checksum, eight of ignored version byte, twenty-four of zero tail, one dead bit. Of 144 bits, 103 describe a crosshair and 41 do not.
Set a crosshair bigger than 25.5 and it wraps
cl_crosshairsize is one unsigned byte divided by ten, so the format tops out at 25.5. There is no clamp, no saturation and no error. The encoder computes size * 10 and lets it overflow.
I encoded the same crosshair at a range of sizes and decoded each result back:
| Size going in | Size coming out |
|---|---|
| 10 | 10 |
| 25.5 | 25.5 |
| 26 | 0.4 |
| 30 | 4.4 |
| 51 | 25.4 |
A crosshair one notch over the ceiling comes out as a crosshair sixty-five times smaller, and the code that carries it is perfectly valid: right length, right alphabet, correct checksum. Nothing anywhere in the pipeline says a value was lost.
cl_crosshairgap has the same shape and a different edge, because it is signed. Byte 127 is a gap of 12.7 and byte 128 is a gap of -12.8, so the wrap there does not shrink the crosshair, it inverts it. cl_crosshairthickness shares the size ceiling at 25.5. cl_crosshair_outlinethickness is divided by two rather than ten and reaches 127.5, which is more outline than anyone has ever wanted.
I measured the codec, not the game. Whether CS2's own generator clamps a console value before encoding it is not something I can check without the game in front of me, and if it does, the ceiling never bites for a code the game produced. What the measurement does establish is that a code from anywhere else - a crosshair-builder site, a script, a config converter - can hand you a valid code for a crosshair nobody chose, and that the format offers no way to notice.
What the checksum actually catches
One byte. Sum the other seventeen, keep the low eight bits, store it at the front. That is the entire integrity check, so I wanted to know how much of one it is.
So I mistyped the example code on purpose, exhaustively: every one of its 25 positions, replaced by each of the 56 other characters in the alphabet. Fourteen hundred codes, each exactly one keystroke away from the real one.
Five of them decoded.
That is 0.36%, against the 5.5 you would predict from a checksum byte behaving like a random draw. The check is doing exactly what one byte can do, and nothing more.
None of the five decoded back to the original crosshair. Every code that got past the checksum got past it while describing something else.
In practice you copy and paste, and none of this touches you. It touches the moment somebody reads a code aloud on stream, or an OCR pass lifts one out of a screenshot, or a config file gets hand-edited. The failure mode is not a code that gets rejected, it is a code that quietly is not the one you were given - which is the same reason the alphabet drops 0 and 1 in the first place.
Valve has never written any of this down
The feature arrived on 16 April 2020, in the CS:GO update that also shipped an experimental Wingman warmup mode and a box of music kits. The announcement is three sentences long: as a spectator you can now view other players' crosshairs, and "See one you like? Click a player on the scoreboard to copy their crosshair."1
No code, no format, no convar list. Six years and one engine later, there is still no Valve document describing the encoding, which is why every fact above comes from an implementation rather than a specification. The one this site's decoder is transcribed from is akiver's, published under MIT.2 An independently written Python decoder arrives at the same layout, says its codes are fully compatible with CS:GO and CS2, and credits an earlier community implementation rather than any document from Valve.3
That is also why the third-party guides disagree about cl_crosshairstyle. The field is three bits wide, and all eight values survive a round trip through the codec, so the format itself takes 0 to 7. Which of those the game draws, and what it calls each one, is not something the code can tell you and not something Valve has published. Any numbered list of style names you find, including ones that sound confident, was written by someone reading the settings menu.
Reading a code somebody handed you
The practical version is short. There is no server on the other end of a crosshair share code and nothing to look up: every setting is in the string, which is why an offline decoder can print your commands and why a code from 2020 still works.
Paste it into the crosshair code decoder to see those lines before you run them, particularly if the code came from anywhere other than the game itself. A checksum that catches 99.6% of typos is a good checksum. It is not a guarantee, and the 0.4% does not announce itself.
Reading the commands is also the way to see what a code will and will not touch when you apply it: what importing a crosshair code actually changes works through the settings it rounds, and the ones it never carries at all.
Sources
Every number in this article traces to a source below. Where a claim could not be sourced, it was cut rather than softened.
- Primary sourceValve Corporation
That Valve shipped spectator crosshair viewing and copying in April 2020, described only as clicking a player on the scoreboard to copy their crosshair, with no mention of a code or of the format behind it.
- Primary sourceSyberiaK
That an independently written decoder reaches the same format, states its share codes are fully compatible with CS:GO and CS2, and credits an earlier community implementation rather than any Valve document.
Topics
- CS2
- Crosshair
- Console Commands
- Encoding
Tools mentioned in this article
- CS2 Crosshair Code Decoder - Decode a CSGO-xxxxx crosshair share code into cl_crosshair console commands.
- CS2 Sensitivity Converter - Convert mouse sensitivity between CS2, Valorant, Apex and more, with eDPI and cm/360.
- CS2 Voice Comms Calculator - Hear specific players in a CS2 or FACEIT demo - upload it to auto-fill players and get the tv_listen_voice_indices commands.
- SteamID Converter - Convert between SteamID64, STEAM_0:Y:Z, SteamID3 [U:1:W], account ID and profile URL.
Get new tools by email
New tools and the occasional deep-dive, about once a month. No spam, no sharing your address, unsubscribe in one click.