Step through the 4-bit Gray code below. Each row shows the decimal value, its standard binary representation, and its Gray code representation. Watch the highlighted bit column — only one bit changes on each step.
<p class="hint">
The 4-bit Gray code visits all 16 values. Use the buttons to step forward or backward. Watch the highlighted bit — <b>exactly one bit flips</b> on every step.
</p>
<div id="table-wrap">
<table id="gray-table">
<thead>
<tr>
<th>Decimal</th>
<th class="b3">Bit 3</th>
<th class="b2">Bit 2</th>
<th class="b1">Bit 1</th>
<th class="b0">Bit 0</th>
<th>Binary</th>
<th>Gray</th>
<th>Formula</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<div class="controls">
<button id="prev" type="button">◄ Prev</button>
<button id="next" type="button">Next ►</button>
<button id="play" type="button">► Auto-play</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
<div class="info" id="info"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: 14px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
#table-wrap { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; min-width: 420px; }
th, td { padding: .3rem .55rem; text-align: center; border: 1px solid #dde3ea; }
th { background: #e8eef3; font-weight: 700; font-size: .82rem; }
td { font-family: ui-monospace, monospace; font-size: .93rem; }
td.dec { font-family: system-ui, sans-serif; font-weight: 600; color: #1d3557; }
td.bin, td.gray, td.formula { letter-spacing: .06em; }
tr.active { background: #e4f0ff; }
td.changed { background: #ffd166; color: #1d1d1d; border-radius: 4px; font-weight: 700; }
td.prev-changed { background: #ffe8a0; }
.b3, .b2, .b1, .b0 { width: 52px; }
.controls { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .8rem; }
button { font: 600 13px system-ui, sans-serif; padding: .4rem .85rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .4; cursor: default; }
.info { margin-top: .55rem; font-size: .88rem; font-weight: 600;
color: #0a7d33; min-height: 1.3em; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Gray code for n bits: Gray(k) = k XOR (k >> 1)
// The reflected binary Gray code visits all 2^n values such that consecutive values differ in exactly one bit.
const BITS = 4;
const SIZE = 1 << BITS; // 16
function grayOf(n) { return n ^ (n >> 1); }
function toBin(n, bits) { return n.toString(2).padStart(bits, '0'); }
// Build the full 4-bit Gray code table
const TABLE = Array.from({ length: SIZE }, (_, i) => ({
dec: i,
bin: toBin(i, BITS),
gray: toBin(grayOf(i), BITS),
grayVal: grayOf(i)
}));
// Verify single-bit-change property (logged to console for STEP 3 verification)
let verified = true;
for (let i = 0; i < SIZE; i++) {
const next = (i + 1) % SIZE;
const diff = TABLE[i].grayVal ^ TABLE[next].grayVal;
const bits = diff.toString(2).split('').filter(b => b === '1').length;
if (bits !== 1) { verified = false; console.error('FAIL at', i, '->', next, 'diff bits:', bits); }
}
console.log('Gray code single-bit-change verified (including wrap):', verified);
console.log('Table:', TABLE.map(r => r.gray).join(' '));
let current = 0;
let prevChanged = -1;
let timer = null;
function changedBit(from, to) {
// returns the bit position (0-3, 0=LSB) that flipped, or -1 if none/multiple
const diff = TABLE[from].grayVal ^ TABLE[to].grayVal;
if (diff === 0) return -1;
const pos = Math.log2(diff);
return Number.isInteger(pos) ? pos : -1;
}
function render() {
const tbody = document.getElementById('tbody');
tbody.innerHTML = '';
const prev = (current - 1 + SIZE) % SIZE;
const cb = changedBit(prev, current);
for (let i = 0; i < SIZE; i++) {
const row = TABLE[i];
const tr = document.createElement('tr');
if (i === current) tr.className = 'active';
// bit cells 3..0
const bitCells = [];
for (let b = BITS - 1; b >= 0; b--) {
const td = document.createElement('td');
const bit = (row.grayVal >> b) & 1;
td.textContent = bit;
// highlight changed bit in the active row
if (i === current && b === cb) td.className = 'changed';
else if (i === prev && b === cb) td.className = 'prev-changed';
bitCells.push(td);
}
const cells = [
Object.assign(document.createElement('td'), { textContent: row.dec, className: 'dec' }),
...bitCells,
Object.assign(document.createElement('td'), { textContent: row.bin, className: 'bin' }),
Object.assign(document.createElement('td'), { textContent: row.gray, className: 'gray' }),
Object.assign(document.createElement('td'), { textContent: i + ' XOR ' + (i >> 1) + ' = ' + row.grayVal, className: 'formula' })
];
cells.forEach(c => tr.appendChild(c));
tbody.appendChild(tr);
}
// scroll active row into view
const activeRow = tbody.querySelector('tr.active');
if (activeRow) activeRow.scrollIntoView({ block: 'nearest' });
document.getElementById('prev').disabled = current === 0;
document.getElementById('next').disabled = current === SIZE - 1;
const info = document.getElementById('info');
if (current > 0) {
const bit = changedBit(current - 1, current);
info.textContent = fmt('Step %current%: only bit %bit% changed (Gray %gray% → %gray2%)', { current: current, bit: bit, gray: TABLE[current - 1].gray, gray2: TABLE[current].gray });
} else {
info.textContent = 'Start: Gray(0) = 0000';
}
}
function step(dir) {
current = Math.max(0, Math.min(SIZE - 1, current + dir));
render();
}
function stopPlay() {
if (timer) { clearInterval(timer); timer = null; }
document.getElementById('play').textContent = '► Auto-play';
}
document.getElementById('prev').addEventListener('click', () => { stopPlay(); step(-1); });
document.getElementById('next').addEventListener('click', () => { stopPlay(); step(1); });
document.getElementById('reset').addEventListener('click', () => {
stopPlay(); current = 0; render();
document.getElementById('info').textContent = '';
});
document.getElementById('play').addEventListener('click', function () {
if (timer) { stopPlay(); return; }
if (current >= SIZE - 1) current = 0;
this.textContent = '⏸ Pause';
timer = setInterval(() => {
if (current >= SIZE - 1) { stopPlay(); return; }
step(1);
}, 700);
});
render();
<p class="hint">
The 4-bit Gray code visits all 16 values. Use the buttons to step forward or backward. Watch the highlighted bit — <b>exactly one bit flips</b> on every step.
</p>
<div id="table-wrap">
<table id="gray-table">
<thead>
<tr>
<th>Decimal</th>
<th class="b3">Bit 3</th>
<th class="b2">Bit 2</th>
<th class="b1">Bit 1</th>
<th class="b0">Bit 0</th>
<th>Binary</th>
<th>Gray</th>
<th>Formula</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
<div class="controls">
<button id="prev" type="button">◄ Prev</button>
<button id="next" type="button">Next ►</button>
<button id="play" type="button">► Auto-play</button>
<button id="reset" type="button" class="ghost">Reset</button>
</div>
<div class="info" id="info"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; font-size: 14px; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
#table-wrap { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; min-width: 420px; }
th, td { padding: .3rem .55rem; text-align: center; border: 1px solid #dde3ea; }
th { background: #e8eef3; font-weight: 700; font-size: .82rem; }
td { font-family: ui-monospace, monospace; font-size: .93rem; }
td.dec { font-family: system-ui, sans-serif; font-weight: 600; color: #1d3557; }
td.bin, td.gray, td.formula { letter-spacing: .06em; }
tr.active { background: #e4f0ff; }
td.changed { background: #ffd166; color: #1d1d1d; border-radius: 4px; font-weight: 700; }
td.prev-changed { background: #ffe8a0; }
.b3, .b2, .b1, .b0 { width: 52px; }
.controls { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .8rem; }
button { font: 600 13px system-ui, sans-serif; padding: .4rem .85rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .4; cursor: default; }
.info { margin-top: .55rem; font-size: .88rem; font-weight: 600;
color: #0a7d33; min-height: 1.3em; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Gray code for n bits: Gray(k) = k XOR (k >> 1)
// The reflected binary Gray code visits all 2^n values such that consecutive values differ in exactly one bit.
const BITS = 4;
const SIZE = 1 << BITS; // 16
function grayOf(n) { return n ^ (n >> 1); }
function toBin(n, bits) { return n.toString(2).padStart(bits, '0'); }
// Build the full 4-bit Gray code table
const TABLE = Array.from({ length: SIZE }, (_, i) => ({
dec: i,
bin: toBin(i, BITS),
gray: toBin(grayOf(i), BITS),
grayVal: grayOf(i)
}));
// Verify single-bit-change property (logged to console for STEP 3 verification)
let verified = true;
for (let i = 0; i < SIZE; i++) {
const next = (i + 1) % SIZE;
const diff = TABLE[i].grayVal ^ TABLE[next].grayVal;
const bits = diff.toString(2).split('').filter(b => b === '1').length;
if (bits !== 1) { verified = false; console.error('FAIL at', i, '->', next, 'diff bits:', bits); }
}
console.log('Gray code single-bit-change verified (including wrap):', verified);
console.log('Table:', TABLE.map(r => r.gray).join(' '));
let current = 0;
let prevChanged = -1;
let timer = null;
function changedBit(from, to) {
// returns the bit position (0-3, 0=LSB) that flipped, or -1 if none/multiple
const diff = TABLE[from].grayVal ^ TABLE[to].grayVal;
if (diff === 0) return -1;
const pos = Math.log2(diff);
return Number.isInteger(pos) ? pos : -1;
}
function render() {
const tbody = document.getElementById('tbody');
tbody.innerHTML = '';
const prev = (current - 1 + SIZE) % SIZE;
const cb = changedBit(prev, current);
for (let i = 0; i < SIZE; i++) {
const row = TABLE[i];
const tr = document.createElement('tr');
if (i === current) tr.className = 'active';
// bit cells 3..0
const bitCells = [];
for (let b = BITS - 1; b >= 0; b--) {
const td = document.createElement('td');
const bit = (row.grayVal >> b) & 1;
td.textContent = bit;
// highlight changed bit in the active row
if (i === current && b === cb) td.className = 'changed';
else if (i === prev && b === cb) td.className = 'prev-changed';
bitCells.push(td);
}
const cells = [
Object.assign(document.createElement('td'), { textContent: row.dec, className: 'dec' }),
...bitCells,
Object.assign(document.createElement('td'), { textContent: row.bin, className: 'bin' }),
Object.assign(document.createElement('td'), { textContent: row.gray, className: 'gray' }),
Object.assign(document.createElement('td'), { textContent: i + ' XOR ' + (i >> 1) + ' = ' + row.grayVal, className: 'formula' })
];
cells.forEach(c => tr.appendChild(c));
tbody.appendChild(tr);
}
// scroll active row into view
const activeRow = tbody.querySelector('tr.active');
if (activeRow) activeRow.scrollIntoView({ block: 'nearest' });
document.getElementById('prev').disabled = current === 0;
document.getElementById('next').disabled = current === SIZE - 1;
const info = document.getElementById('info');
if (current > 0) {
const bit = changedBit(current - 1, current);
info.textContent = fmt('Step %current%: only bit %bit% changed (Gray %gray% → %gray2%)', { current: current, bit: bit, gray: TABLE[current - 1].gray, gray2: TABLE[current].gray });
} else {
info.textContent = 'Start: Gray(0) = 0000';
}
}
function step(dir) {
current = Math.max(0, Math.min(SIZE - 1, current + dir));
render();
}
function stopPlay() {
if (timer) { clearInterval(timer); timer = null; }
document.getElementById('play').textContent = '► Auto-play';
}
document.getElementById('prev').addEventListener('click', () => { stopPlay(); step(-1); });
document.getElementById('next').addEventListener('click', () => { stopPlay(); step(1); });
document.getElementById('reset').addEventListener('click', () => {
stopPlay(); current = 0; render();
document.getElementById('info').textContent = '';
});
document.getElementById('play').addEventListener('click', function () {
if (timer) { stopPlay(); return; }
if (current >= SIZE - 1) current = 0;
this.textContent = '⏸ Pause';
timer = setInterval(() => {
if (current >= SIZE - 1) { stopPlay(); return; }
step(1);
}, 700);
});
render();
Notice that ordinary binary counting frequently flips multiple bits at once (3→4 flips three bits). The Gray code never does. At every step, exactly one bit flips — that is the whole point. The demo also shows the closed-form conversion formula: Gray = n XOR (n >> 1).
Comments
Loading comments...