Skip to main content
Gaming Tools

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.

By 9 min read
Article title card. Five grey bars in a row above an amber crosshair drawn as four bars and a centre dot, with the line: 25 characters, 18 bytes, 21 commands

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.
Diagram of a crosshair share code. The code CSGO-Cn37R-YE7vo-pLCAL-aURmZ-z6zkG sits above a row of eighteen hex byte cells. Byte 0 is amber and bracketed as the checksum, byte 1 is greyed and bracketed as never read, bytes 2 to 14 are bracketed as the 21 cl_crosshair values, and bytes 15 to 17 are greyed and bracketed as always zero. A dashed line leads from byte 13 down to that byte opened into eight bits, labelled T shape, use alpha, weapon gap and centre dot across the top four, three bits bracketed as cl_crosshairstyle equals 2, and the last bit marked unused.
Thirteen bytes do the work, and byte 13 does four jobs at once. The first byte is a checksum, the second is never read, and the last three have never held 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:

Thirteen of the 21 values. The eight left out are the four dynamic-split controls and the remaining toggles.
ConVarValue
cl_crosshairstyle2
cl_crosshairsize4.6
cl_crosshairthickness1.2
cl_crosshairgap-1.3
cl_crosshaircolor5 (custom)
cl_crosshaircolor_r/g/b175, 81, 213
cl_crosshairalpha137
cl_crosshair_drawoutline1, thickness 2
cl_crosshairdot1
cl_crosshair_t1

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:

Every row past the ceiling lands exactly 25.6 low, which is 256 tenths going round one byte.
Size going inSize coming out
1010
25.525.5
260.4
304.4
5125.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.

Screenshot of the ZeroUtil CS2 Crosshair Code Decoder. The share code CSGO-Cn37R-YE7vo-pLCAL-aURmZ-z6zkG is in the input field, and below it a Console commands panel lists twenty-one cl_crosshair lines in alphabetical order, from cl_crosshair_drawoutline 1 down to cl_fixedcrosshairgap 3.
The same code as the diagram, decoded by the tool. Twenty-one lines out of twenty-five characters, and no request left the tab to produce them.

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.

  1. 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.

  2. Primary sourceakiver

    The 57-character dictionary, the leading checksum byte, and the byte-to-conVar mapping that every field in this article was read out of.

  3. 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

Tools mentioned in this article

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.

Related articles

Title card on a dark grid. Kicker GAMING TOOLS, headline WHAT A CROSSHAIR CODE CHANGES, and the line: it writes 21 cl_crosshair lines and leaves the rest alone. A panel shows a block of 21 small amber marks above a dashed divider, with a few grey marks set apart below it.
Gaming Tools

What Importing a CS2 Crosshair Code Actually Changes

A crosshair share code writes 21 cl_crosshair variables, truncates a few, and never touches your scoped or observer crosshair. Why an imported crosshair can still look off.

Article title card. Two columns of five dots either side of a dashed dividing line, the left column amber and the right grey, with the line: the server decides who was recorded
Gaming Tools

How to Hear the Enemy Team's Voice in a CS2 Demo

Whether you can hear the enemy in a CS2 demo depends on what the server recorded, not on your console. Which demos carry it, and the mask for one side.

Article title card. A loudspeaker icon struck through by a thick amber line, with the line: the demo has no voice in it
Gaming Tools

Why You Can't Hear Voice in Your CS2 Demo (Fixes)

Can't hear voice comms in a CS2 or FACEIT demo? The real reasons - no voice in MM/Premier demos, unset tv_listen_voice_indices, commands resetting - and the fixes.