A number, in pieces

Every integer you write in code — 42, 255, 1024 — is stored as a sequence of binary digits inside your machine. Each digit is a single bit: either 0 (OFF) or 1 (ON). The number 13, for instance, is secretly 1101: an 8 worth of value, a 4, no 2, and a 1. These aren't abstract — they're physical voltage levels in silicon, and every bitwise algorithm you'll ever write manipulates them directly.

Here's why this matters for you as a programmer: when you write flags & 0xFF or n >> 1, you're not doing math on the number as a whole — you're reaching in and touching individual bits. The CPU doesn't see “thirteen.” It sees a row of switches. And once you see them too, operations that looked like cryptic incantations become simple mechanical actions.

Crack open the number below and see the switches hiding inside it.

You just decomposed an integer into its constituent bits. Each position has a fixed weight — powers of two, doubling as you move left. That positional weight system is the foundation everything else builds on.

12022142282316243225642612827

Toggle it

Knowing the decomposition is one thing. Controlling it is another. Each bit is an independent switch — flipping it ON adds that position's weight to the total, flipping it OFF subtracts it. The relationship between a bit's position and its contribution to the decimal value isn't immediately obvious, especially for higher positions where the weights jump from 16 to 32 to 64. Build your intuition by driving the switches yourself and watching the decimal value respond in real time.

128
64
32
16
8
4
2
1
=0

Notice how each bit's influence doubles as you move left. Bit 0 is worth 1, bit 3 is worth 8, bit 7 is worth 128. This exponential growth is why bitwise operations are so powerful — a single bit flip at position k changes the value by exactly 2^k, nothing more.

128bit 7127bits 0-6128 > 127

What do operators DO?

You've seen individual bits. But the real power comes from combining two bit patterns with an operator. TypeScript gives you three core operators — & (AND), | (OR), ^ (XOR) — and each one compares bits at the same position across two numbers, producing a new bit from the pair.

But what does each operator actually do to a pair of bits? You could read a truth table from a textbook — four rows, three columns, memorize and move on. But that's recognition, not understanding. Instead, you'll build all three truth tables from scratch by predicting each result before you see it. Twelve predictions, and by the end you'll own the rules because you constructed them.

1 / 24
AND
0
&
0
=?

0 & 0 = ?

Three operators, twelve predictions. You now own the truth tables — not because you memorized them, but because you constructed them. AND passes a 1 only when both inputs agree. OR passes when either is 1. XOR passes when the inputs differ. These three rules are the entire vocabulary of bitwise programming — and in the next screen, you'll discover why knowing them forward isn't enough.

Reverse the crime

Knowing how operators work forward is necessary but not sufficient. In real code, you'll often see the result of a bitwise operation and need to reason backward — what operator produced this output from these inputs? Think of it like forensics: you arrive at the scene, you see the before and after, and you have to figure out which tool did the work. The three operators from the previous screen — &, |, ^ — are your suspect lineup.

This is harder than it sounds, because AND and OR can produce identical results for certain input pairs. If both input bits are 1 and the output bit is 1, that's consistent with all three operators. The only way to distinguish them is to find a position where they'd diverge — a bit position where the input pair would produce different outputs depending on which operator was applied. The detective work happens at those disagreement points: a 0 input becoming 1 in the output rules out AND immediately, because 0 & anything = 0. A 1 input becoming 0 rules out OR, because 1 | anything = 1.

Examine the before-and-after bit patterns below and deduce which operator was used. Look for the fingerprint — the bit position that only one operator could explain.

Puzzle 1 / 2
Puzzle 1: Find which columns changed between before and after.
BEFORE
=42
AFTER
=238
Some bits turned ON. Which columns changed? Tap them.

Working backward forced you to think about what each operator can't do. AND can never turn a 0 into a 1. OR can never turn a 1 into a 0. When you see a bit flip in a direction that only one operator permits, that's your fingerprint.

The plot twist

These puzzles are different — and the difference is disorienting at first. Look at the before and after bit patterns: some bits flip ON, others flip OFF, in the same operation applied to the same pair of numbers. That shouldn't be possible with the operators you've used so far.

Think about it: AND can only turn bits OFF. If both inputs are 1, the output is 1 (no change). If either input is 0, the output is 0 (either no change or a flip to OFF). AND never introduces a 1 that wasn't there — it only preserves or destroys them. OR has the mirror limitation: it can only turn bits ON. 0 | 0 = 0 (no change), everything else is 1 (either no change or a flip to ON). Neither operator can produce a result where some bits went up and some went down relative to the first input.

But that's exactly what the evidence below shows. There's one operator left — the one whose truth table you built on Screen 3 — and it's the only one whose behavior is bidirectional enough to explain these results. Look at the evidence and figure out which operator can produce bits that change both ways.

Puzzle 1 / 2
Puzzle 1: Find which columns changed between before and after.
BEFORE
=178
AFTER
=142
Bits changed in BOTH directions. Which columns flipped?

XOR is the chameleon. Where the mask has a 1, the bit flips — regardless of its starting state. 0 becomes 1, 1 becomes 0. Where the mask has 0, the bit passes through unchanged. This bidirectional flipping power makes XOR uniquely useful — and it's the operator behind some of the most elegant algorithms in computer science.

XOR mask: 1 1011→00→1^

In code

Three operators, three behaviors — and each one maps to a single TypeScript expression. a & b for AND, a | b for OR, a ^ b for XOR. The puzzles you just solved are the exact same logic that runs inside if (flags & MASK) checks, permission-setting with flags | WRITE, and toggling with state ^ TOGGLE. Connect the operator to the code expression, and you'll see how compact bitwise TypeScript really is.

1 / 3

Bits only appeared in the transformation

const result = a ___ mask
flagsvariableyour data&operatorthe action0xFFmaskthe selector

Each puzzle distilled into one line. That compression is the promise of bitwise programming: complex set operations, permission checks, and state toggles — all expressed as single infix operators on integers.

Design a puzzle

You've solved puzzles and filled in code. Now prove you own the material by going the other direction: design a before-and-after puzzle that can ONLY be solved with XOR. This is the hardest test — if AND or OR could explain your puzzle, it isn't tricky enough. You need to craft a scenario where bits flip in both directions, which uniquely fingerprints XOR. Think about which bit patterns force AND and OR out of the running.

Build a BEFORE/AFTER pair that ONLY XOR can solve. Bits must flip both ways.
BEFORE
=0
? operator + ? mask
AFTER
=0

Bits as Toggles

Truth TablesAND, OR, XOR3 operators discovered
Bit Archaeologyevidence + deduction + construction4 puzzles solved
Code Bridgemapped operators to code3 expressions filled
Puzzle Designcreated a XOR-only puzzle
You started by cracking open 182 into its 8 bits — a row of switches, each controlling a power of 2. Then you built three truth tables from scratch, predicting every single output before seeing it. AND keeps, OR sets, XOR toggles. The archaeology puzzles made you reverse-engineer the operator from evidence: which direction did the bits move? That is the core reasoning skill for bit manipulation. Finally, you designed a puzzle that ONLY XOR could solve — proving you understand why each operator has a unique behavioral signature.

Three operators, three behaviors

const result = a & maskfilter
const result = a | maskset
const result = a ^ masktoggle

When to reach for each

&Extract or check specific bits
|Set flags or combine masks
^Toggle, swap, or detect differences