Inside every phone, laptop and tablet is a stamp-sized metal square called the CPU (for “central processing unit”). A CPU is conceptually a simple device: it loops through memory looking for instructions to execute, each one in the form of a number that corresponds to a (very) specific command. Instruction 538 might mean “add the contents of registers A and B and store them in C” or “if register C contains a 0, skip D instructions ahead”. (A register is like a variable, but CPUs only have a small, fixed number of them, and only for numbers.) CPUs are fussy: you can’t give variables friendly names, but have to know exactly where and when everything will be stored, ahead of time. Doing that planning – turning your variable names and if statements into registers and instruction sequences – is the job of the compiler, which turns your meticulously-composed text program into inscrutable machine code for the CPU.
The compiler’s job is made tougher by the existence of many different kinds of processor, each with different numbers of registers, types and names of instructions, and so on. And that’s before you get to tasks like opening files or displaying graphics, which are handled by the operating system. Windows, Mac, Linux and others have their own mechanisms even when running on identical hardware. So a language that wants to run everywhere has its work cut out.
WebAssembly (or wasm) solves this with a virtual CPU specification. It’s not quite like any real CPU, and no hardware can run wasm code as-is. But it’s close enough that you can translate it for a given CPU on the fly, just before running it. Compile your program once, and the WebAssembly runtime on your watch, Raspberry Pi, toaster or whatever will handle it.