An operating system built from scratch in C and x86 assembly that runs in your browser.
SimpleOS is a learning project — a from-scratch kernel that implements processes, virtual memory, syscalls, pipes, signals, a filesystem, and a shell, all in roughly 4,500 lines of code. It boots as a real ISO image via GRUB, and a bundled web app lets you run the entire OS in-browser using the v86 x86 emulator. No toolchain, no VM, just open a page and you're inside a working OS.
This isn't a production operating system. It's a proof of concept and a learning tool — built to understand how operating systems actually work by writing one from the ground up.
Open the live demo in your browser — no install required:
Try SimpleOS in your browser →
The web app boots the ISO in a v86 emulator with a CRT-styled terminal UI.
Preemptive round-robin scheduler with 10-tick quantum
- Two-level paging with copy-on-write
fork()
- 18 syscalls via
int 0x80(fork, execve, wait, pipe, dup2, kill, and more)
- Unix-style signals (SIGINT, SIGKILL, SIGSTOP, etc.)
- In-memory ramfs with pre-populated
/bin/*
- VGA text mode, PS/2 keyboard, PIT timer, virtual terminal switching (Alt+F1–F4)
- 32-bit ELF loader for user-mode binaries
- Freestanding userspace: shell, grep, wc, hello
Runs entirely in-browser via the v86 x86 emulator
Subsystem Key files What it does Boot boot.s, grub.cfg, linker.ld Multiboot2 entry, GDT, stack setup, jumps to kernel_mainKernel core kernel.c Initializes every subsystem, creates test processes, starts scheduler Processes process.c, scheduler.c Round-robin preemptive scheduling (10-tick quantum), kernel threads and user processes, up to 64 Context switching context_switch.s Saves/restores register state, interrupt-return trampoline for user mode Memory pmm.c, vmm.c, kmalloc.c Bitmap physical allocator, two-level paging with copy-on-write, bump-pointer heap Syscalls syscall.c 18 syscalls via int 0x80— fork, execve, wait, pipe, dup2, kill, and moreIPC pipe.c, signal.c 512-byte circular pipes, Unix-style signals (SIGINT, SIGKILL, SIGSTOP, etc.) Filesystem fs.c In-memory ramfs — 64 nodes, 512KB of 512-byte blocks, pre-populated with /bin/*Drivers terminal.c, keyboard.c, timer.c, vt.c VGA text mode, PS/2 keyboard with Ctrl+C/Z, PIT timer, virtual terminal switching (Alt+F1–F4) ELF loader elf.c Loads 32-bit ELF binaries into user address space User mode usermode.c, tss.c Ring 0 → Ring 3 via IRET, TSS for kernel stack on interrupts Userspace shell.c, grep.c, wc.c, hello.c Freestanding programs built as ELF, converted to C byte arrays, embedded in the kernel image Web demo Emulator.tsx, CRTFrame.tsx v86 emulator with asset preflight, CRT-styled terminal with animated ASCII background GRUB (Multiboot2) → _start src/arch/i386/boot.s load GDT, set up stack → kernel_main src/kernel/kernel.c init all subsystems → scheduler_enable src/kernel/scheduler.c start round-robin loop → idle process PID 0 runs when nothing else is readyThe kernel is linked at 1MB (
linker.ld). The heap lives at 2–3.5MB. User processes get virtual addresses starting at 128MB, with stacks below 3GB.
Address Region 0x00100000(1MB)Kernel code ( .multiboot,.text,.data,.bss)0x00200000(2MB)Kernel heap start 0x00380000(3.5MB)Kernel heap end 0x00400000(4MB)Physical memory allocator starts here 0x04000000(64MB)End of kernel identity-mapped window 0x08000000(128MB)User virtual address space begins 0xBFFFF000(~3GB)User stack top (grows down) 0xC0000000(3GB)Kernel boundary
All syscalls use
int 0x80with arguments ineax(number),ebx,ecx,edx. Userspace wrappers live inulib.h.
# Name Description 1 exitTerminate process 2 writeWrite to fd (terminal, file, or pipe) 3 readRead from fd 4 getpidGet current PID 5 sleepSleep N milliseconds 6 sbrkGrow user heap 7 forkFork process (COW page tables) 8 waitWait for child, reap zombie 9 execveLoad and run ELF binary 10 psList all processes 11 openOpen file 12 closeClose fd 13 statFile metadata 14 mkdirCreate directory 15 readdirRead directory entries 16 killSend signal to process 17 pipeCreate pipe pair 18 dup2Duplicate fd
The shell supports:
Built-ins:
help,ps,echo,cd,clear,exit,jobs,bg,fg,kill
- External programs:
/bin/hello,/bin/grep,/bin/wc(via fork + execve)
- Pipes:
echo hello | wc
- Redirection:
cmd > file,cmd < file
- Background jobs:
cmd &, thenjobs,fg,bg
- Job control: Ctrl+C (SIGINT), Ctrl+Z (SIGTSTP)
History: up/down arrows (10 entries)
src/ arch/i386/ boot.s, asm_functions.s, context_switch.s, tss.c, usermode.c boot/ exceptions.c (ISR/IDT setup) kernel/ kernel.c, process.c, scheduler.c, syscall.c, panic.c mm/ kmalloc.c, pmm.c, vmm.c drivers/ terminal.c, keyboard.c, timer.c, ports.c, vt.c fs/ fs.c ipc/ pipe.c, signal.c lib/ elf.c, string.c include/ headers mirroring src/ layout userspace/ shell.c, grep.c, wc.c, hello.c, ulib.h, Makefile boot/grub/ grub.cfg grub-bios/ pre-built GRUB i386-pc modules for ISO creation web/ Next.js app (Emulator.tsx, CRTFrame.tsx, page.tsx) scripts/ dev-shell.sh, setup-toolchain.sh .devcontainer/ Dockerfile + devcontainer.json
The kernel targets 32-bit freestanding x86. The Makefile cross-compiles with
i686-elf-gccand producessimpleos.isoviagrub-mkrescue. Userspace programs are compiled separately (userspace/Makefile), then converted to C byte arrays withxxd -iand#included into the kernel's filesystem init.Open the repo in VS Code, Cursor, or Zed — the devcontainer config has everything pre-installed.
make cd web && npm install && npm run devSee
scripts/setup-toolchain.shfor install instructions. You need:
i686-elf-gcc/i686-elf-as
x86_64-elf-grub-mkrescue(for ISO creation)
qemu-system-i386(for local testing) make # build simpleos.iso make run # boot in QEMU./build.sh # builds simpleos.iso → web/public/os/The Dockerfile extends a cross-compiler image with GRUB and xorriso. You can also get an interactive shell with
scripts/dev-shell.sh.The web app expects three assets:
web/public/os/simpleos.iso— built bymakeor./build.sh
web/public/bios/seabios.bin— SeaBIOS ROM
web/public/bios/vgabios.bin— VGA BIOS ROM
If anything is missing, the preflight check shows exactly what's absent instead of booting into a broken state.
cd web npm install npm run dev # starts on port 3500 npm run build:os # rebuild kernel and copy ISO in one stepStart here:
src/kernel/kernel.c—kernel_main()initializes every subsystem in order and is the best map of how the pieces connect.From there:
Boot:
boot.s→ how the CPU gets from GRUB tokernel_main
- Interrupts:
asm_functions.s→ ISR stubs, IDT/GDT loading, paging enable
- Processes:
process.c→ PCB structure, creation, destruction
- Scheduling:
scheduler.c→ preemptive round-robin, context switch calls
- Memory:
vmm.c→ page tables, COW fork, address space management
- Syscalls:
syscall.c→int 0x80dispatch, all 18 implementations
- Userspace:
ulib.h→ syscall wrappers that user programs call
Shell:
shell.c→ pipes, redirection, job control in ~500 lines
verify_os.pychecks code integrity without compiling — include dependencies, function signatures, syscall coverage, signal integration, and common issues like duplicate includes or unfinished TODOs.
Contributions are welcome! Please read CONTRIBUTING.md before opening a pull request.
Quick guide:
- Fork the repo and create a branch:
git checkout -b feat/your-feature
- Make your changes and ensure the build passes:
make
- Run the verifier:
python3 verify_os.py
Open a pull request with a clear description of what changed and why
Please follow the existing code style (K&R-adjacent C, descriptive names, comments on non-obvious logic). For larger changes, open an issue first to discuss the approach.
To report a bug or request a feature, open an issue.
For security vulnerabilities, please see SECURITY.md and do not open a public issue.
MIT © raintree-technology
raintree-technology/SimpleOS
Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|