Notes › Tech Notes › The color picker parse gate accepts only six-digit hex, making the three-digit branch unreachable on the page

The color picker parse gate accepts only six-digit hex, making the three-digit branch unreachable on the page

The color picker HEX gate admits only six-digit hex: the three-digit shorthand computes when lifted out but scores zero hits across all 33554432 scans, while parseInt applies no 0-255 clamp, so 300 grows into an oversized hex the page then rejects.

The parse gate and its six exits

This page has only six exits: the four output slots c-hex, c-rgb, c-hsl, c-css, plus the native color control's value and the preview swatch; the color control measures 1, with its default written straight into the markup. The template is 5039 bytes / 4981 code units / 115 CRLF lines, no BOM, one script block of 2105 bytes, 9 named functions, and fetch/XHR/localStorage/axios each hit 0; the trailing updateAll('#39FF14') runs one calculation on first paint.

The HEX box's gate is, verbatim, /^#?[0-9a-fA-F]{6}$/: anchored, trimmed first, and accepting only six hexadecimal digits. Seven invalid inputs, "#f0a", "12345", "1234567", "gggggg", "#1234 56", the empty string, and "#FFF", all return false; the six slots keep their old values silently, with zero warning and the four cells still showing the last successful result.

Three-digit shorthand: computable when lifted out, unreachable on the page

In hexToRgb the branch if (hex.length===3) hex.split('').map(c=>c+c) pads a short value. Lift the function out verbatim and feed it "f0a", and doubling gives "ff00aa" and [255,0,170], the correct value; for contrast, skipping the doubling and running parseInt("f0a",16) gives 3850, which folds out [0,15,10]. But that branch cannot be reached by any input on this page: sweep the whole region the gate lets through, feeding all 16777216 colors with and without the hash, 33,554,432 entries in total, one by one into fromHex; length===6 hits 33,554,432 times while length===3 hits 0 times, in 115.334 seconds.

Way fedInputResult
Lifted out, fed directly"f0a"[255,0,170] (doubled ff00aa)
Page's fromHex"#f0a", "#FFF"regex false, six stale slots
Full sweep of the gate region33554432 entrieslength===3 hits 0 times
Two paths for the same string "f0a" hexToRgb, fed directly doubles to [255,0,170] page fromHex gate regex false, six stale slots full gate sweep: 33554432 fed length===3 hits 0 times 3-digit pad is dead branch

Figure 1: the two paths for three-digit shorthand and the sweep result

No clamping: oversized HEX and strings carrying a minus sign

The regex in fromRgb is neither anchored nor trimmed: decimals, minus signs, misplaced spaces, full-width parentheses, a missing closing parenthesis, a fourth argument, and rgba/hsla are each rejected; yet "xrgb(1,2,3)y", wrapped in junk, still gets accepted. parseInt has no 0-255 clamp: 256 turns into "100", 300 into "12c", 999 into "3e7", 1000 into "3e8", all three-digit segments, and padStart(2,'0') only pads, never truncates, so an 8-10 code-unit oversized HEX goes straight to screen; rgb(1000,1000,1000) yields "#3E83E83E8". Feeding that batch back into fromHex returns false for every one, so the page will not accept the HEX it just wrote out.

The optional %? in fromHsl makes "hsl(110,100%,54%)" and "hsl(110,100,54)" produce a byte-for-byte identical result; still no clamp: hsl(400,150%,150%) outputs [191,319,574], which becomes "#BF13F23E", and hsl(110,200%,20%) outputs the negative channels [-17,153,-51], writing "#-1199-33", minus sign and all, plainly into c-hex and c-css.

Rounding, identity rate, and the five write-outs

The three Math.round calls in rgbToHsl on the default color rgb(57,255,20) give [111,100,54]; the un-rounded intermediates are h*360=110.5532 and l*100=53.92156862745098. The intro cheat sheet's hsl(110,100,54) differs cell by cell by +1,0,0, and feeding it back into this page's HSL box gives "#3CFF14" / rgb(60, 255, 20); the HSL box placeholder's 109 differs by +2,0,0.

The HEX to HSL to HEX forward sweep covers the whole space, 16,777,216 entries (hex = n.toString(16).padStart(6,'0')), leaving 1,775,241 identical = 10.5813%; among the 15,001,975 non-identical ones the largest single-channel gap of 1 step holds 9,123,157 entries, and exactly one channel off by 1 holds 4,024,886. The reverse HSL to HEX to HSL takes h 0-359 with s/l stepping by 2, 936,360 entries total, 444,318 identical = 47.4516%; l stays identical in all 936,360, h in 562,431, and s in 635,760.

The upper-casing in updateAll happens only in the two places c-hex and c-css (toUpperCase counted 2); the color wheel value and the preview swatch take the string as-is. After fromHex("AbCdEf") the four cells are uppercase while the wheel keeps "#AbCdEf". The Color Converter entry page is this very template under test.

Round-trip identity: forward and reverse forward HEX-HSL-HEX same 1775241 / 16777216 identity rate 10.5813% reverse HSL-HEX-HSL same 444318 / 936360 identity rate 47.4516% no 0-255 / 0-360 clamp oversized HEX shown, then rejected

Figure 2: round-trip identity statistics and the no-clamp path

Control counts and copy cross-check

By grep: the palette is 12 cells, the UU.copy buttons number 3 (ids c-hex, c-rgb, c-hsl, given one by one; the read-only --color box is not among them); the three keywords alpha / rgba / hsla each hit 0. Typing "#39ff14" one character at a time is 7 keys, and only the 7th triggers; RGB takes 15 keys and HSL 17 keys, the last key firing in each case; there is no intermediate state that shows a half-built color while typing. The copy cross-check runs 15 items: [match] 5, [partial match] 5, [conflict] 5, the conflicts clustered on RGBA/HSLA/Alpha and the cheat-sheet's hue of 110 against the computed 111. For the conversion formulas and the hand-calculation path, the earlier article Color Format Conversion treats the three formats as the thing to explain, while this page only tests the gate.

Common questions

Q: Why does typing "#f0a" do nothing?

The gate lets through only six-digit hex strings, so the whole family of three-digit shorthands is blocked and the four cells keep their old values; the padding branch is expressible inside the function but cannot be reached on the page.

Q: Why does rgb(300,300,300) compute to something else?

parseInt applies no clamp, so 300 turns into "12c" and forms an oversized HEX that reaches the screen, then hexToRgb slices it by position and folds it back into other channel values.

Q: Is the identity rate of 10.5813% a mistake?

It is the rounding loss from the three Math.round calls in rgbToHsl, not a crash and not an error; to run one more comparison, go to the CSS Gradient Generator and walk the same color value through it.

📎 Related articles

Editing the Salt Changes Nothing Until You Click: Three-Segment Concatenation and Multiline Input Measured on the MD5 Page 2026-11-08 A 60-bit Timestamp Leaves 28 Positions on This Page: Shifts and Masks in the UUID Generator v1 Branch, Measured Millisecond by Millisecond 2026-11-08 The Count Reads 5 or 1: Three Switches and Zero-Width Matches Measured in Batch Find and Replace 2026-11-07 Clear One Box and 0 Cards Show: Silent Failure, Skipping Row Labels and 990 Unreduced Rows 2026-11-07 text-line-ops: delete right gives an empty string, cut right hands back the whole line 2026-11-07