export const page = { title: 'Subnet Calculator', template: () => `

IP Subnet Calculator

Enter an IPv4 address, e.g. 192.168.1.0

/24
/1 — huge /8 /16 /24 /30 — tiny

Usable Hosts

254

Subnet Mask

255.255.255.0

Relative Network Size (logarithmic)
2 hosts (/30) 2 billion hosts (/1)

Network ID

-

network address

First Host

-

1st usable address

Last Host

-

last usable address

Broadcast

-

send to all devices

What does this mean?

Network Address

The first address — identifies the network itself. No device can use this address.

Host Addresses

All addresses in between — assignable to devices like PCs, servers, or printers.

Broadcast Address

The last address — packets sent here are delivered to every device in the network.

Typical Networks — click to try

What is a subnet, exactly?

The neighbourhood analogy

Think of a city. Every house has a full address: district + house number. A subnet works the same way — every IP address is split into two parts.

Network part (district)

192.168.1.___

All devices in the same subnet share this part — like neighbours on the same street.

Host part (house number)

___.42

Each device gets a unique number within the network.

What does the slash mean?

An IP address is made up of exactly 32 bits (ones and zeros) under the hood. The /24 says: “the first 24 bits belong to the network, the remaining 8 bits are the host number.”

192.168.1.42 = 11000000.10101000.00000001.00101010
/24 mask = 11111111.11111111.11111111.00000000
    ←——— network (24 bits) ———→ ← host (8 bits) →

8 host bits = 28 = 256 addresses, 254 usable (minus network ID and broadcast).

Why do subnets exist?

Organisation

Group devices logically — e.g. keep office PCs separate from servers or guest Wi-Fi.

Security

Isolate networks from each other — malware on the guest network can't reach corporate systems.

Efficiency

Broadcast traffic stays within the subnet — no unnecessary noise for the rest of the network.

`, init() { // ─── Shared helpers ──────────────────────────────────────────────────────── function ipToBinary(ip) { return ip.split('.').map(o => parseInt(o, 10).toString(2).padStart(8, '0')).join(''); } function binaryToIp(b) { const parts = []; for (let i = 0; i < 32; i += 8) parts.push(parseInt(b.slice(i, i + 8), 2)); return parts.join('.'); } function cidrToMask(cidr) { return binaryToIp('1'.repeat(cidr) + '0'.repeat(32 - cidr)); } function maskToCidr(mask) { const b = ipToBinary(mask); if (/^1*0*$/.test(b)) return b.replace(/0+$/, '').length; return null; } function isValidIP(ip) { return /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/.test(ip); } function calcSubnet(ip, cidr) { const ipBin = ipToBinary(ip); const maskBin = '1'.repeat(cidr) + '0'.repeat(32 - cidr); let netBin = ''; for (let i = 0; i < 32; i++) netBin += (parseInt(ipBin[i]) & parseInt(maskBin[i])).toString(); const hostBits = 32 - cidr; const bcBin = netBin.slice(0, cidr) + '1'.repeat(hostBits); const netNum = parseInt(netBin, 2); const bcNum = parseInt(bcBin, 2); let hosts, first, last; if (hostBits >= 2) { hosts = Math.pow(2, hostBits) - 2; first = binaryToIp((netNum + 1).toString(2).padStart(32, '0')); last = binaryToIp((bcNum - 1).toString(2).padStart(32, '0')); } else if (cidr === 31) { hosts = 2; first = binaryToIp(netBin); last = binaryToIp(bcBin); } else { hosts = 1; first = binaryToIp(netBin); last = binaryToIp(netBin); } return { network: binaryToIp(netBin), broadcast: binaryToIp(bcBin), mask: cidrToMask(cidr), hosts, first, last, netBin, ipBin, maskBin, cidr, hostBits }; } // ─── Mode toggle ────────────────────────────────────────────────────────── const beginnerEl = document.getElementById('beginner-mode'); const proEl = document.getElementById('pro-mode'); const btnBeg = document.getElementById('btn-beginner'); const btnPro = document.getElementById('btn-pro'); const activeClass = 'px-6 py-2 rounded-lg text-sm font-semibold transition-all duration-200 bg-gradient-to-r from-purple-600 to-pink-600 text-white shadow-lg'; const inactiveClass = 'px-6 py-2 rounded-lg text-sm font-semibold transition-all duration-200 text-gray-400 hover:text-gray-200'; function setMode(mode) { const isBeg = mode === 'beginner'; beginnerEl.classList.toggle('hidden', !isBeg); proEl.classList.toggle('hidden', isBeg); btnBeg.className = isBeg ? activeClass : inactiveClass; btnPro.className = isBeg ? inactiveClass : activeClass; } btnBeg.addEventListener('click', () => setMode('beginner')); btnPro.addEventListener('click', () => setMode('pro')); // ─── Beginner mode ──────────────────────────────────────────────────────── const begIpInput = document.getElementById('beg-ip'); const begSlider = document.getElementById('beg-slider'); const begCidrLabel = document.getElementById('beg-cidr-label'); const begBar = document.getElementById('beg-bar'); const begHostsCount = document.getElementById('beg-hosts-count'); const begMaskDisplay = document.getElementById('beg-mask-display'); const begNetwork = document.getElementById('beg-network'); const begFirst = document.getElementById('beg-first'); const begLast = document.getElementById('beg-last'); const begBroadcast = document.getElementById('beg-broadcast'); const begExplain = document.getElementById('beg-explain-text'); function updateBeginner() { const ip = begIpInput.value.trim(); const cidr = parseInt(begSlider.value, 10); begCidrLabel.textContent = `/${cidr}`; // bar: log scale via host-bit count const barPct = Math.max(3, ((32 - cidr) / 31) * 100); begBar.style.width = barPct + '%'; if (!isValidIP(ip)) return; const r = calcSubnet(ip, cidr); begHostsCount.textContent = r.hosts.toLocaleString('en'); begMaskDisplay.textContent = r.mask; begNetwork.textContent = r.network; begFirst.textContent = r.first; begLast.textContent = r.last; begBroadcast.textContent = r.broadcast; let sizeDesc; if (cidr <= 8) sizeDesc = 'This is a massive network — only found in large data centres or at internet service providers.'; else if (cidr <= 12) sizeDesc = 'This is a very large network, typical for big enterprises or campus environments.'; else if (cidr <= 16) sizeDesc = 'This is a large network, common in mid-sized companies or universities.'; else if (cidr <= 20) sizeDesc = 'This is a medium-sized network, e.g. for a large office building or campus.'; else if (cidr <= 24) sizeDesc = 'This is a typical home or small office network — the default on most routers.'; else if (cidr <= 27) sizeDesc = 'This is a small network, e.g. for a single department or server cluster.'; else sizeDesc = 'This is a very small network, usually used for direct point-to-point links.'; begExplain.innerHTML = ` A /${cidr} network reserves ${cidr} bits for the network address and leaves ${r.hostBits} bits for hosts. That gives ${r.hosts.toLocaleString('en')} usable IP addresses (2${r.hostBits}−2, since the network ID and broadcast are reserved).

${sizeDesc} `; } begSlider.addEventListener('input', updateBeginner); begIpInput.addEventListener('input', updateBeginner); document.querySelectorAll('.beg-example').forEach(btn => { btn.addEventListener('click', () => { begIpInput.value = btn.dataset.ip; begSlider.value = btn.dataset.cidr; updateBeginner(); }); }); updateBeginner(); // ─── Expert mode ────────────────────────────────────────────────────────── const form = document.getElementById('subnet-form'); const ipInput = document.getElementById('ip-address'); const cidrInput = document.getElementById('cidr'); const errorEl = document.getElementById('subnet-error'); const resultsEl = document.getElementById('results'); function showInlineError(msg) { errorEl.textContent = msg; errorEl.classList.toggle('hidden', !msg); } function coloredBits(bits, cidr, hostChar) { let html = ''; for (let i = 0; i < 32; i++) { if (i > 0 && i % 8 === 0) html += '.'; const isNet = i < cidr; const ch = hostChar && !isNet ? hostChar : bits[i]; html += `${ch}`; } return html; } function renderBinary(r) { const maskBits = '1'.repeat(r.cidr) + '0'.repeat(r.hostBits); document.getElementById('bin-ip').innerHTML = coloredBits(r.ipBin, r.cidr, null); document.getElementById('bin-mask').innerHTML = coloredBits(maskBits, r.cidr, null); document.getElementById('bin-net').innerHTML = coloredBits(r.netBin, r.cidr, 'x'); document.getElementById('bin-legend').innerHTML = ` network bit (${r.cidr})    ` + ` host bit (${r.hostBits})  —  ` + `x = any host address within this network`; } function calculate() { showInlineError(null); const ip = ipInput.value.trim(); const cidrRaw = cidrInput.value.trim(); if (!isValidIP(ip)) { showInlineError('Please enter a valid IPv4 address.'); return; } let cidr; if (cidrRaw.includes('.')) { if (!isValidIP(cidrRaw)) { showInlineError('Please enter a valid subnet mask.'); return; } cidr = maskToCidr(cidrRaw); if (cidr === null) { showInlineError('Invalid subnet mask — must be a contiguous sequence of ones (e.g. 255.255.255.0).'); return; } } else { cidr = parseInt(cidrRaw, 10); if (isNaN(cidr) || cidr < 0 || cidr > 32) { showInlineError('Please enter a valid CIDR value (0–32).'); return; } } const r = calcSubnet(ip, cidr); document.getElementById('network-address').textContent = r.network; document.getElementById('broadcast-address').textContent = r.broadcast; document.getElementById('subnet-mask').textContent = r.mask; document.getElementById('host-count').textContent = r.hosts.toLocaleString('en'); document.getElementById('first-host').textContent = r.first; document.getElementById('last-host').textContent = r.last; renderBinary(r); resultsEl.classList.remove('hidden'); } form.addEventListener('submit', e => { e.preventDefault(); calculate(); }); document.querySelectorAll('.example-link').forEach(link => { link.addEventListener('click', () => { ipInput.value = link.dataset.ip; cidrInput.value = link.dataset.cidr; calculate(); window.scrollTo({ top: 0, behavior: 'smooth' }); }); }); } };