Chris Collins

Lab · Playground

C and C++ in the browser

Real Clang, compiled to WebAssembly, building and running C and C++ programs entirely inside your browser.

Code blocks on this page are editable — change them and press Run.

These playgrounds compile with a full Clang/LLVM toolchain that itself runs as WebAssembly (YoWASP Clang). Press Run and your code is compiled to a WebAssembly program, linked against the WASI C library, and executed in a Web Worker. Nothing is sent to a server.

The first run downloads the compiler — about 50 MB — so give it a moment; the browser caches it for every later run and page. Programs can read standard input: press stdin, type some text, then Run.

The Mandelbrot set in 60 columns of text
#include <stdio.h>

int main(void) {
    const char *shades = " .:-=+*#%@";
    for (int y = -12; y <= 12; y++) {
        for (int x = -39; x <= 20; x++) {
            double cr = x / 20.0, ci = y / 10.0, zr = 0, zi = 0;
            int i = 0;
            while (i < 50 && zr * zr + zi * zi < 4) {
                double t = zr * zr - zi * zi + cr;
                zi = 2 * zr * zi + ci;
                zr = t;
                i++;
            }
            putchar(shades[i == 50 ? 9 : i % 9]);
        }
        putchar('\n');
    }
    return 0;
}
A tiny wc: press stdin, paste some text, and run
#include <ctype.h>
#include <stdio.h>

int main(void) {
    long lines = 0, words = 0, chars = 0;
    int c, in_word = 0;
    while ((c = getchar()) != EOF) {
        chars++;
        if (c == '\n') lines++;
        if (isspace(c)) in_word = 0;
        else if (!in_word) { in_word = 1; words++; }
    }
    if (chars == 0) {
        puts("Standard input is empty — press stdin, type something, and run again.");
        return 0;
    }
    printf("%ld lines, %ld words, %ld characters\n", lines, words, chars);
    return 0;
}
Modern C++: lambdas, structured bindings, and the standard library
#include <algorithm>
#include <iostream>
#include <map>
#include <numeric>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> words{"wasm", "clang", "browser", "llvm", "wasi", "compile", "link", "run"};

    std::ranges::sort(words, [](const auto& a, const auto& b) {
        return a.size() != b.size() ? a.size() < b.size() : a < b;
    });
    for (const auto& w : words) std::cout << w << ' ';
    std::cout << '\n';

    std::map<std::size_t, int> by_length;
    for (const auto& w : words) by_length[w.size()]++;
    for (const auto& [len, n] : by_length) std::cout << len << " letters: " << n << '\n';

    auto total = std::accumulate(words.begin(), words.end(), std::size_t{0},
                                 [](std::size_t t, const auto& w) { return t + w.size(); });
    std::cout << "total letters: " << total << '\n';
}
Undefined behaviour is still undefined: try changing the array size
#include <stdint.h>
#include <stdio.h>
#include <string.h>

int main(void) {
    // WebAssembly has a flat, bounds-checked linear memory, but C inside it does not:
    // writing past an array silently scribbles over whatever is next in memory.
    struct { char name[8]; uint32_t balance; } account = { "chris", 100 };
    printf("before: %s has %u\n", account.name, account.balance);
    strcpy(account.name, "christopher");   /* 12 bytes into an 8-byte array */
    printf("after:  %s has %u\n", account.name, account.balance);
    return 0;
}

How it works

  1. The page starts a Web Worker and loads Clang and LLD (LLVM’s linker), both built for WebAssembly, plus the WASI system headers and libraries.

  2. Clang compiles your file to a wasm32-wasi executable in an in-memory file system.

  3. The worker runs that executable with a small WASI shim (browser_wasi_shim) that connects standard input, output, and error to the page.

Stop terminates the worker, so even for (;;); can be interrupted. Threads, sockets, and files on your disk are not available: WASI programs only see what the page gives them.