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 fed | Input | Result |
|---|---|---|
| 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 region | 33554432 entries | length===3 hits 0 times |
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.
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.