Created
January 8, 2026 15:06
-
-
Save bmorphism/b2e9017e88f77949bf1025b48177b79e to your computer and use it in GitHub Desktop.
Gay-TOFU: Error-correcting codec demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Hamming Swarm Error-Correcting Codec</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 100%); | |
| color: #e0e0e0; | |
| font-family: 'SF Mono', 'Consolas', monospace; | |
| padding: 20px; | |
| } | |
| .container { max-width: 1200px; margin: 0 auto; } | |
| h1 { | |
| color: #6CEC13; | |
| text-align: center; | |
| font-size: 2em; | |
| margin-bottom: 10px; | |
| } | |
| .subtitle { | |
| text-align: center; | |
| color: #888; | |
| margin-bottom: 30px; | |
| font-size: 0.9em; | |
| } | |
| .panel { | |
| background: rgba(0,0,0,0.6); | |
| border: 1px solid #333; | |
| border-radius: 8px; | |
| padding: 20px; | |
| margin: 20px 0; | |
| } | |
| .row { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 20px; | |
| margin: 20px 0; | |
| } | |
| textarea { | |
| width: 100%; | |
| min-height: 100px; | |
| background: #1a1a1a; | |
| color: #e0e0e0; | |
| border: 1px solid #444; | |
| border-radius: 4px; | |
| padding: 10px; | |
| font-family: 'SF Mono', monospace; | |
| font-size: 14px; | |
| resize: vertical; | |
| } | |
| button { | |
| background: linear-gradient(135deg, #851BE4 0%, #37C0C8 100%); | |
| color: white; | |
| border: none; | |
| padding: 10px 20px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| font-weight: bold; | |
| margin: 5px; | |
| transition: transform 0.1s; | |
| } | |
| button:hover { transform: scale(1.05); } | |
| button:active { transform: scale(0.95); } | |
| .btn-danger { | |
| background: linear-gradient(135deg, #ff4444 0%, #cc0000 100%); | |
| } | |
| .color-strip { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 2px; | |
| margin: 10px 0; | |
| } | |
| .color-cell { | |
| width: 30px; | |
| height: 30px; | |
| border: 1px solid #333; | |
| border-radius: 3px; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 12px; | |
| cursor: pointer; | |
| transition: transform 0.1s; | |
| } | |
| .color-cell:hover { | |
| transform: scale(1.2); | |
| z-index: 10; | |
| border: 2px solid #6CEC13; | |
| } | |
| .stats { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 15px; | |
| margin: 15px 0; | |
| } | |
| .stat-box { | |
| background: rgba(255,255,255,0.05); | |
| padding: 15px; | |
| border-radius: 6px; | |
| border: 1px solid #333; | |
| } | |
| .stat-label { | |
| color: #888; | |
| font-size: 11px; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| } | |
| .stat-value { | |
| font-size: 24px; | |
| font-weight: bold; | |
| color: #6CEC13; | |
| margin-top: 5px; | |
| } | |
| .error-viz { | |
| background: #1a1a1a; | |
| padding: 15px; | |
| border-radius: 6px; | |
| margin: 10px 0; | |
| font-family: monospace; | |
| font-size: 13px; | |
| } | |
| .error { color: #ff4444; font-weight: bold; } | |
| .corrected { color: #6CEC13; font-weight: bold; } | |
| .unchanged { color: #888; } | |
| label { | |
| display: block; | |
| color: #6CEC13; | |
| margin: 10px 0 5px 0; | |
| font-weight: bold; | |
| font-size: 12px; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| } | |
| .slider-container { | |
| margin: 15px 0; | |
| } | |
| input[type="range"] { | |
| width: 100%; | |
| height: 6px; | |
| background: #333; | |
| outline: none; | |
| border-radius: 3px; | |
| } | |
| input[type="range"]::-webkit-slider-thumb { | |
| appearance: none; | |
| width: 18px; | |
| height: 18px; | |
| background: #6CEC13; | |
| cursor: pointer; | |
| border-radius: 50%; | |
| } | |
| .binary-display { | |
| font-family: 'Courier New', monospace; | |
| background: #0a0a0a; | |
| padding: 10px; | |
| border-radius: 4px; | |
| font-size: 12px; | |
| margin: 10px 0; | |
| overflow-x: auto; | |
| } | |
| .bit { | |
| display: inline-block; | |
| margin: 0 2px; | |
| padding: 2px 4px; | |
| background: #1a1a1a; | |
| border-radius: 2px; | |
| } | |
| .bit-flipped { | |
| background: #ff4444; | |
| color: white; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>🌈 Hamming Swarm Error-Correcting Codec</h1> | |
| <div class="subtitle"> | |
| Bijective color encoding with automatic error detection and correction via Hamming distance | |
| </div> | |
| <!-- Encode Panel --> | |
| <div class="panel"> | |
| <h2 style="color: #851BE4;">📤 Encode Message</h2> | |
| <label>Plain Text Message</label> | |
| <textarea id="plain-text" placeholder="Enter your message here... (A-Z only, spaces ignored)">HELLO WORLD</textarea> | |
| <div class="slider-container"> | |
| <label>Encoding Seed: <span id="encode-seed-value">42</span></label> | |
| <input type="range" id="encode-seed" min="1" max="999999" value="42" oninput="updateSeed('encode')"> | |
| </div> | |
| <button onclick="encodeMessage()">🔒 Encode with Gay-TOFU Colors</button> | |
| <div id="encoded-output"></div> | |
| </div> | |
| <!-- Corruption Panel --> | |
| <div class="panel"> | |
| <h2 style="color: #37C0C8;">💥 Simulate Transmission Errors</h2> | |
| <div class="slider-container"> | |
| <label>Error Rate: <span id="error-rate-value">10%</span></label> | |
| <input type="range" id="error-rate" min="0" max="50" value="10" oninput="updateErrorRate()"> | |
| </div> | |
| <button onclick="corruptMessage()">⚡ Introduce Random Bit Flips</button> | |
| <button onclick="resetCorruption()" class="btn-danger">🔄 Reset Corruption</button> | |
| <div id="corruption-viz"></div> | |
| </div> | |
| <!-- Decode Panel --> | |
| <div class="panel"> | |
| <h2 style="color: #6CEC13;">📥 Decode & Error Correction</h2> | |
| <button onclick="decodeMessage()">🔓 Decode with Error Correction</button> | |
| <div id="decoded-output"></div> | |
| </div> | |
| <!-- Statistics --> | |
| <div class="panel"> | |
| <h2 style="color: #6CEC13;">📊 Statistics</h2> | |
| <div class="stats"> | |
| <div class="stat-box"> | |
| <div class="stat-label">Original Length</div> | |
| <div class="stat-value" id="stat-orig-len">0</div> | |
| </div> | |
| <div class="stat-box"> | |
| <div class="stat-label">Bits Flipped</div> | |
| <div class="stat-value" id="stat-flips">0</div> | |
| </div> | |
| <div class="stat-box"> | |
| <div class="stat-label">Errors Corrected</div> | |
| <div class="stat-value" id="stat-corrected">0</div> | |
| </div> | |
| <div class="stat-box"> | |
| <div class="stat-label">Correction Rate</div> | |
| <div class="stat-value" id="stat-rate">0%</div> | |
| </div> | |
| <div class="stat-box"> | |
| <div class="stat-label">GF(3) Trit Sum</div> | |
| <div class="stat-value" id="stat-trit">0</div> | |
| </div> | |
| <div class="stat-box"> | |
| <div class="stat-label">Hamming Distance</div> | |
| <div class="stat-value" id="stat-hamming">0</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| // Gay-TOFU Color System | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| const PHI2 = 1.3247179572447460; // Plastic constant | |
| function hslToRgb(h, s, l) { | |
| const c = (1 - Math.abs(2 * l - 1)) * s; | |
| const hp = h / 60; | |
| const x = c * (1 - Math.abs((hp % 2) - 1)); | |
| let r1 = 0, g1 = 0, b1 = 0; | |
| if (hp >= 0 && hp < 1) [r1, g1, b1] = [c, x, 0]; | |
| else if (hp >= 1 && hp < 2) [r1, g1, b1] = [x, c, 0]; | |
| else if (hp >= 2 && hp < 3) [r1, g1, b1] = [0, c, x]; | |
| else if (hp >= 3 && hp < 4) [r1, g1, b1] = [0, x, c]; | |
| else if (hp >= 4 && hp < 5) [r1, g1, b1] = [x, 0, c]; | |
| else if (hp >= 5 && hp < 6) [r1, g1, b1] = [c, 0, x]; | |
| const m = l - c / 2; | |
| return [r1 + m, g1 + m, b1 + m]; | |
| } | |
| function plasticColor(n, seed) { | |
| const seedNorm = (seed % 1000000) / 1000000; | |
| const h = (((seedNorm + n / PHI2) % 1.0) * 360); | |
| const s = (((seedNorm + n / (PHI2 * PHI2)) % 1.0) * 0.5) + 0.5; | |
| return hslToRgb(h, s, 0.55); | |
| } | |
| function rgbToHex(r, g, b) { | |
| const toHex = x => Math.round(x * 255).toString(16).padStart(2, '0'); | |
| return `#${toHex(r)}${toHex(g)}${toHex(b)}`; | |
| } | |
| function tritFromHue(hue) { | |
| const sector = Math.floor(hue / 120) % 3; | |
| return sector === 0 ? 1 : sector === 1 ? 0 : -1; | |
| } | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| // Alphabet & Binary Encoding | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ🌈'.split(''); | |
| function letterToIndex(letter) { | |
| return ALPHABET.indexOf(letter.toUpperCase()); | |
| } | |
| function indexToLetter(index) { | |
| return ALPHABET[index]; | |
| } | |
| function letterToBinary(letter) { | |
| const index = letterToIndex(letter); | |
| return index === -1 ? null : index.toString(2).padStart(5, '0'); | |
| } | |
| function binaryToLetter(binary) { | |
| const index = parseInt(binary, 2); | |
| return index < ALPHABET.length ? ALPHABET[index] : null; | |
| } | |
| function hammingDistance(bin1, bin2) { | |
| let distance = 0; | |
| for (let i = 0; i < 5; i++) { | |
| if (bin1[i] !== bin2[i]) distance++; | |
| } | |
| return distance; | |
| } | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| // Encoding State | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| let encodedData = []; | |
| let corruptedData = []; | |
| let currentSeed = 42; | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| // UI Updates | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| function updateSeed(type) { | |
| const value = document.getElementById(`${type}-seed`).value; | |
| document.getElementById(`${type}-seed-value`).textContent = value; | |
| currentSeed = parseInt(value); | |
| } | |
| function updateErrorRate() { | |
| const value = document.getElementById('error-rate').value; | |
| document.getElementById('error-rate-value').textContent = `${value}%`; | |
| } | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| // Encoding | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| function encodeMessage() { | |
| const text = document.getElementById('plain-text').value | |
| .toUpperCase() | |
| .replace(/[^A-Z]/g, ''); | |
| if (text.length === 0) { | |
| alert('Please enter a message with A-Z letters'); | |
| return; | |
| } | |
| encodedData = []; | |
| let tritSum = 0; | |
| const letters = text.split(''); | |
| let html = '<label>Encoded Color Sequence</label>'; | |
| html += '<div class="color-strip">'; | |
| letters.forEach((letter, i) => { | |
| const index = letterToIndex(letter); | |
| const [r, g, b] = plasticColor(index + 1, currentSeed); | |
| const color = rgbToHex(r, g, b); | |
| const binary = letterToBinary(letter); | |
| const h = (((currentSeed % 1000000) / 1000000 + (index + 1) / PHI2) % 1.0) * 360; | |
| const trit = tritFromHue(h); | |
| tritSum += trit; | |
| encodedData.push({ | |
| letter, | |
| index, | |
| binary, | |
| color, | |
| trit | |
| }); | |
| html += `<div class="color-cell" style="background: ${color}; color: ${getContrastColor(color)}" | |
| title="${letter} = ${binary} (trit=${trit})">${letter}</div>`; | |
| }); | |
| html += '</div>'; | |
| html += `<div class="binary-display">`; | |
| encodedData.forEach(d => { | |
| html += `<span class="bit">${d.binary}</span> `; | |
| }); | |
| html += `</div>`; | |
| document.getElementById('encoded-output').innerHTML = html; | |
| // Update stats | |
| document.getElementById('stat-orig-len').textContent = letters.length; | |
| document.getElementById('stat-trit').textContent = `${tritSum} (mod 3 = ${((tritSum % 3) + 3) % 3})`; | |
| // Reset corruption | |
| corruptedData = []; | |
| document.getElementById('corruption-viz').innerHTML = ''; | |
| document.getElementById('decoded-output').innerHTML = ''; | |
| } | |
| function getContrastColor(hexColor) { | |
| const r = parseInt(hexColor.slice(1, 3), 16); | |
| const g = parseInt(hexColor.slice(3, 5), 16); | |
| const b = parseInt(hexColor.slice(5, 7), 16); | |
| const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255; | |
| return luminance > 0.5 ? '#000' : '#fff'; | |
| } | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| // Corruption | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| function corruptMessage() { | |
| if (encodedData.length === 0) { | |
| alert('Please encode a message first'); | |
| return; | |
| } | |
| const errorRate = parseInt(document.getElementById('error-rate').value) / 100; | |
| corruptedData = JSON.parse(JSON.stringify(encodedData)); | |
| let totalFlips = 0; | |
| corruptedData.forEach(item => { | |
| const binaryArray = item.binary.split(''); | |
| item.flips = []; | |
| for (let i = 0; i < 5; i++) { | |
| if (Math.random() < errorRate) { | |
| binaryArray[i] = binaryArray[i] === '0' ? '1' : '0'; | |
| item.flips.push(i); | |
| totalFlips++; | |
| } | |
| } | |
| item.corruptedBinary = binaryArray.join(''); | |
| item.corruptedLetter = binaryToLetter(item.corruptedBinary); | |
| }); | |
| // Visualize corruption | |
| let html = '<label>Corrupted Sequence (red = bit flips)</label>'; | |
| html += '<div class="error-viz">'; | |
| corruptedData.forEach((item, idx) => { | |
| const orig = encodedData[idx].binary.split(''); | |
| const corr = item.corruptedBinary.split(''); | |
| html += `<div style="margin: 5px 0;">`; | |
| html += `<b>${item.letter}</b> → <b class="${item.flips.length > 0 ? 'error' : 'unchanged'}">${item.corruptedLetter}</b>: `; | |
| for (let i = 0; i < 5; i++) { | |
| const className = orig[i] !== corr[i] ? 'bit-flipped' : ''; | |
| html += `<span class="bit ${className}">${corr[i]}</span>`; | |
| } | |
| if (item.flips.length > 0) { | |
| html += ` <span class="error">(${item.flips.length} flips, d=${hammingDistance(item.binary, item.corruptedBinary)})</span>`; | |
| } | |
| html += `</div>`; | |
| }); | |
| html += '</div>'; | |
| document.getElementById('corruption-viz').innerHTML = html; | |
| document.getElementById('stat-flips').textContent = totalFlips; | |
| } | |
| function resetCorruption() { | |
| corruptedData = []; | |
| document.getElementById('corruption-viz').innerHTML = ''; | |
| document.getElementById('decoded-output').innerHTML = ''; | |
| document.getElementById('stat-flips').textContent = '0'; | |
| document.getElementById('stat-corrected').textContent = '0'; | |
| document.getElementById('stat-rate').textContent = '0%'; | |
| document.getElementById('stat-hamming').textContent = '0'; | |
| } | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| // Decoding with Error Correction | |
| // ═══════════════════════════════════════════════════════════════════════════════ | |
| function decodeMessage() { | |
| const dataToUse = corruptedData.length > 0 ? corruptedData : encodedData; | |
| if (dataToUse.length === 0) { | |
| alert('Please encode a message first'); | |
| return; | |
| } | |
| let correctedCount = 0; | |
| let totalHamming = 0; | |
| const results = []; | |
| dataToUse.forEach((item, idx) => { | |
| const binaryToCorrect = item.corruptedBinary || item.binary; | |
| // Minimum distance decoding | |
| let minDistance = Infinity; | |
| let bestMatch = null; | |
| ALPHABET.forEach(letter => { | |
| const validBinary = letterToBinary(letter); | |
| const dist = hammingDistance(binaryToCorrect, validBinary); | |
| if (dist < minDistance) { | |
| minDistance = dist; | |
| bestMatch = letter; | |
| } | |
| }); | |
| const wasCorrupted = item.flips && item.flips.length > 0; | |
| const wasCorrected = wasCorrupted && bestMatch === encodedData[idx].letter; | |
| if (wasCorrected) correctedCount++; | |
| if (wasCorrupted) totalHamming += minDistance; | |
| results.push({ | |
| original: encodedData[idx].letter, | |
| received: item.corruptedLetter || item.letter, | |
| corrected: bestMatch, | |
| wasCorrupted, | |
| wasCorrected, | |
| distance: minDistance | |
| }); | |
| }); | |
| // Visualize decoding | |
| let html = '<label>Decoded Message with Error Correction</label>'; | |
| html += '<div class="error-viz">'; | |
| results.forEach(r => { | |
| html += `<div style="margin: 5px 0;">`; | |
| if (r.wasCorrupted) { | |
| html += `<b class="unchanged">${r.original}</b> → `; | |
| html += `<b class="error">${r.received}</b> → `; | |
| html += `<b class="${r.wasCorrected ? 'corrected' : 'error'}">${r.corrected}</b> `; | |
| html += r.wasCorrected ? | |
| `<span class="corrected">✓ CORRECTED (d=${r.distance})</span>` : | |
| `<span class="error">✗ FAILED (d=${r.distance})</span>`; | |
| } else { | |
| html += `<b class="unchanged">${r.original}</b> → <b class="corrected">${r.corrected}</b> `; | |
| html += `<span class="unchanged">✓ No errors</span>`; | |
| } | |
| html += `</div>`; | |
| }); | |
| html += '</div>'; | |
| html += `<label>Final Decoded Text</label>`; | |
| html += `<textarea readonly style="font-size: 18px; text-align: center; font-weight: bold;">${results.map(r => r.corrected).join('')}</textarea>`; | |
| document.getElementById('decoded-output').innerHTML = html; | |
| // Update stats | |
| const corruptedCount = results.filter(r => r.wasCorrupted).length; | |
| const correctionRate = corruptedCount > 0 ? | |
| Math.round((correctedCount / corruptedCount) * 100) : 100; | |
| document.getElementById('stat-corrected').textContent = `${correctedCount}/${corruptedCount}`; | |
| document.getElementById('stat-rate').textContent = `${correctionRate}%`; | |
| document.getElementById('stat-hamming').textContent = | |
| corruptedCount > 0 ? (totalHamming / corruptedCount).toFixed(2) : '0'; | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment