If you've ever read a hex colour in CSS, debugged a bitwise operation, or stared at a Linux file permission like chmod 755, you've worked with number bases — whether you realised it or not.
This guide covers the four bases every developer encounters, when each one shows up, and how to convert between them without breaking a sweat.
Why Do Different Number Bases Exist?
Computers store everything as binary (base-2). Humans prefer decimal (base-10). Hexadecimal and octal exist as convenient bridges — they're easy for humans to read while being trivially convertible to and from binary.
The key insight: hex is just a shorthand for binary. Every hex digit maps exactly to 4 binary digits. Every octal digit maps to 3 binary digits. No fuzzy conversion required.
Base 10 — Decimal (what you already know)
Digits: 0–9. Position represents a power of 10.
``
1,234 = (1 × 10³) + (2 × 10²) + (3 × 10¹) + (4 × 10⁰)
= 1000 + 200 + 30 + 4
`
You've used this your entire life. Nothing special here. The reason decimal isn't universal in computing is that it requires more hardware to implement than binary.
Base 2 — Binary (how computers actually store data)
Digits: 0 and 1. Position represents a power of 2.
<code>
<p>1010 (binary) = (1 × 2³) + (0 × 2²) + (1 × 2¹) + (0 × 2⁰)</p>
<p>= 8 + 0 + 2 + 0</p>
<p>= 10 (decimal)</p>
</code>
Where you'll see binary:
- Bit flags and permissions: chmod 755
→ rwxr-xr-x → 111 101 101 in binary - Bitwise operations in JavaScript: 5 & 3
→ 101 & 011 = 001 → 1 - Memory addresses and hardware registers
- Network subnet masks: 255.255.255.0
→ 11111111.11111111.11111111.00000000
Binary in JavaScript:
`javascript
// Write binary literals with 0b prefix (ES2015+)
const flags = 0b1010; // 10 in decimal
// Convert decimal to binary
(10).toString(2); // "1010"
// Convert binary string to decimal
parseInt("1010", 2); // 10
// Bitwise AND to check a flag
const READ_FLAG = 0b0001;
const WRITE_FLAG = 0b0010;
const userPerms = 0b0011;
if (userPerms & READ_FLAG) console.log("Can read");
if (userPerms & WRITE_FLAG) console.log("Can write");
`
Base 16 — Hexadecimal (the developer's best friend)
Digits: 0–9 and A–F (where A=10, B=11, C=12, D=13, E=14, F=15). Position represents a power of 16.
<code>
<p>FF (hex) = (15 × 16¹) + (15 × 16⁰)</p>
<p>= 240 + 15</p>
<p>= 255 (decimal)</p>
</code>
The hex-to-binary shortcut: Each hex digit maps to exactly 4 binary digits. Memorise this table once and you'll never struggle with the conversion again:
| Hex | Binary | Decimal |
|-----|--------|---------|
| 0 | 0000 | 0 |
| 1 | 0001 | 1 |
| 4 | 0100 | 4 |
| 8 | 1000 | 8 |
| A | 1010 | 10 |
| F | 1111 | 15 |
So #FF5733 (an orange-red colour in CSS) expands to:
- FF → 11111111 (red channel: 255)
- 57 → 01010111 (green channel: 87)
- 33 → 00110011 (blue channel: 51)
Where you'll see hex:
- CSS colours: #2f855a
, #rgba(0,0,0,0.5) - Memory addresses: 0x7fff5fbff990
- Character codes: é
(é in Unicode) - SHA/MD5 hashes: 5eb63bbbe01eeed093cb22bb8f5acdc3
- Colour values in image data
- Network MAC addresses: 00:1B:44:11:3A:B7
Hex in JavaScript:
`javascript
// Write hex literals with 0x prefix
const MAX_BYTE = 0xFF; // 255
// Convert decimal to hex
(255).toString(16); // "ff"
(255).toString(16).toUpperCase(); // "FF"
// Convert hex string to decimal
parseInt("FF", 16); // 255
parseInt("2f855a", 16); // 3113306
// CSS colour manipulation
function hexToRgb(hex) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return { r, g, b };
}
hexToRgb('#2f855a'); // { r: 47, g: 133, b: 90 }
`
Base 8 — Octal (mainly for Unix permissions)
Digits: 0–7. Position represents a power of 8.
<code>
<p>755 (octal) = (7 × 8²) + (5 × 8¹) + (5 × 8⁰)</p>
<p>= 448 + 40 + 5</p>
<p>= 493 (decimal)</p>
</code>
The octal-to-binary shortcut: Each octal digit maps to exactly 3 binary digits.
| Octal | Binary |
|-------|--------|
| 0 | 000 |
| 7 | 111 |
| 5 | 101 |
| 4 | 100 |
So 755 in octal → 111 101 101 in binary → rwxr-xr-x (owner: read/write/execute, group: read/execute, world: read/execute).
Where you'll see octal:
- Unix file permissions: chmod 644 file.txt
(owner: rw, group/world: r only) - Legacy JavaScript string escapes: "\141"
= "a" (avoid in modern code) - Some network protocol specifications
- Older assembly and embedded systems code
Octal in JavaScript:
`javascript
// Modern: use 0o prefix (ES2015+)
const perms = 0o755;
// Older code used leading zero — this is confusing, avoid it:
// const perms = 0755; // deprecated! Strict mode throws an error
// Convert decimal to octal
(493).toString(8); // "755"
// Convert octal string to decimal
parseInt("755", 8); // 493
`
Converting Between Bases — Quick Reference
| From \ To | Binary | Octal | Decimal | Hex |
|-----------|---------|---------------|---------------|--------------|
| Binary | — | Group by 3 | Work it out | Group by 4 |
| Octal | Split to 3 binary each | — | Work it out | → Binary → Group by 4 |
| Decimal | Divide by 2 repeatedly | Divide by 8 | — | Divide by 16 |
| Hex | Split to 4 binary each | → Binary → Group by 3 | Work it out | — |
The "group by 4" and "group by 3" conversions are the fast path. For example:
- 5A
(hex) → 0101 1010 (binary) → 01 011 010 → 132 (octal) — no arithmetic needed
Practical Conversions in Code
`javascript
// The universal approach: go through decimal
const num = 255; // decimal starting point
num.toString(2); // "11111111" (binary)
num.toString(8); // "377" (octal)
num.toString(10); // "255" (decimal — same)
num.toString(16); // "ff" (hex)
// Going the other way
parseInt("11111111", 2); // 255
parseInt("377", 8); // 255
parseInt("ff", 16); // 255
`
One function to rule them all:
`javascript
function convertBase(value, fromBase, toBase) {
return parseInt(value, fromBase).toString(toBase);
}
convertBase("ff", 16, 2); // "11111111" (hex to binary)
convertBase("755", 8, 16); // "1ed" (octal to hex)
convertBase("1010", 2, 10); // "10" (binary to decimal)
`
Tricky Edge Cases
Leading zeros in binary: 00001111 and 1111 are both 15. Leading zeros are cosmetic — add them when aligning bits for readability.
Large numbers: JavaScript's built-in parseInt and toString work up to 32-bit integers safely. Beyond that, use BigInt:
<code>javascript
<p>BigInt("0x" + "ff".repeat(8)).toString(10); // handles 64-bit hex values</p>
</code>
Signed vs unsigned: Be careful with bitwise operations in JavaScript — they operate on 32-bit signed integers. 0xFFFFFFFF >>> 0 gives 4294967295 (unsigned), but 0xFFFFFFFF | 0 gives -1` (signed).
Quick Reference — When To Use Each Base
| Base | Best for |
|------|----------|
| Decimal | Human-readable numbers, API responses, user-facing values |
| Hex | Colours, memory addresses, hashes, binary data representation |
| Binary | Bit flags, bitwise ops, understanding hardware specs |
| Octal | Unix file permissions (that's about it in modern code) |
For quick conversions between all four bases at once — without any JavaScript — the SnappyTools Number Base Converter handles binary, octal, decimal, and hex in a single page. Type in any field and all others update live. Free, no sign-up, runs in your browser.
Originally published at https://snappytools.app/number-base-converter/