Open-source project
rebane2001/x86CSS avatar
rebane2001/x86CSS

x86CSS runs 8086 machine code through a CSS animation clock

x86CSS is a working CSS-only x86 CPU/emulator/computer. No JavaScript required.

939 stars24 forksHTMLGPL-3.0

At a glance

What is it?
x86CSS is a working CSS-only x86 emulator that executes real 8086 machine code compiled from C, with no JavaScript required for the program to run. The author is direct about its practical value, which is close to none, and about how much of the architecture is actually implemented.
Who is it for?
Adopt x86CSS if you want to understand how a CPU can be driven by a stylesheet, or if you want a concrete artefact to build a talk or a blog post around, since it is a working implementation rather than a concept.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 143 days ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Real 8086 machine code, compiled from C by GCC

The claim in the README is deliberately literal. x86CSS is described as a working CSS-only x86 CPU/emulator/computer, and the text adds that what you are looking at is a C program compiled using GCC into native 8086 machine code being executed fully within CSS.

That is the part that distinguishes it from a visual trick. The bytes running are the bytes a compiler produced. The architecture is the original 16-bit x86 of the Intel 8086, and the README is upfront that this is not what your own processor runs, since modern x86 is 64-bit and has a bunch of useful extensions.

The emulation is scoped by method rather than by specification. The author describes the approach: write programs you wanted to run in C, compile them in GCC with various levels of optimization, and then implement every instruction that turned out to be needed. The result is that the implemented instruction set is exactly the set required by the programs the author wanted to run, with the coverage checked against real compiler output rather than against a manual.

The repository layout matches the story. There is a c/ directory for the C sources, build_c.py to compile them, build_css.py to generate the stylesheet, base_template.html as the handwritten shell the generated CSS is placed into, and x86-instructions-rebane.json holding the instruction definitions. The final artefact, x86css.html, is committed at the top level, which is the page you can open in a browser.

The project is licensed GPL-3.0 and the primary language of the repository is listed as HTML, which is an accurate description of a file that is mostly a stylesheet.

Why CSS can hold a program: an animation drives the clock

The mechanism that makes this work is a clock, and the CSS implementation of that clock is the interesting part.

The CSS clock uses an animation combined with style container queries, which means you do not need to interact with anything for the program to run. A CSS animation advances on its own timeline, and the browser samples it to produce the current time. That sampled value becomes the program counter, the CPU state is encoded in the document, and each instruction step is a change in the document that the next animation frame picks up.

That is the whole trick, and it explains the constraints the README describes. There is a script tag on the site, but it is there to provide a clock to the CSS, only to make the entire thing faster and more stable, and the CSS also has a JS-less clock implementation so that disabling scripts still lets it run. JavaScript is not required.

The alternative the author rejects is instructive. A hover-based clock, such as the one in Jane Ori's CPU Hack, is fast and stable but requires holding the mouse on the screen, and the author notes that some people claim a hover-based approach does not count as Turing complete, which is why this demo was made fully functional with zero user input.

So the design trades speed and stability for autonomy. An animation-driven clock runs without input but is described as a bit slower and less stable as a result. That is a real engineering trade, and it is the same trade in every CSS-driven project: the browser's rendering pipeline becomes the clock source.

Chromium only, and a style tag is the one hard requirement

There is a compatibility story here that is worth reading before you try anything.

The README asks whether you still need HTML. The answer given is not really, well, kind of. The entire CPU runs in just CSS and does not require any HTML code, but there is no way to load the CSS without a style tag, so that much is required. In Firefox it is possible to load CSS with no HTML, but the demo currently works only in Chromium-based browsers.

So the practical requirement is a Chromium-based browser and a style element in the document. The no-HTML trick exists in Firefox, but the demo does not target it.

That constraint comes straight from the mechanism. The clock relies on style container queries, and container queries are evaluated as part of style resolution, so the browser needs to support them and needs to apply them on a schedule the program can sample. A browser that resolves styles only on layout or on a different cadence will not advance the program the same way.

The project is hosted at lyra.horse/x86css, and the README links a live version there. The repository's default branch is named mane, and the top-level x86css.html is the built page.

One more build detail matters if you want to read the source. The README says the author just writes CSS, that the CSS in base_template.html is handwritten in Sublime Text, but that for the more repetitive parts of the code a Python script was written. So the stylesheet is not entirely machine generated, and the generated parts are the repetitive ones, which is a sensible split for something of this size.

Writing your own program: memory limits and I/O addresses

You can run your own software in the emulator, but you have to compile it yourself, and the build has a fixed shape.

If you already have 8086 assembly, the instructions are to clone the repo, put the assembly in a file called program.bin, and put the path to the _start() function in program.start as a number. Once that is set, you run build_css.py with Python 3, with no dependencies required, and the output will be in x86css.html.

For C code, the toolchain is gcc-ia16, which the README links to a gitlab project where you can build it yourself or install it from a PPA for Ubuntu. The build_c.py script does the build with the correct flags and also makes the program.bin and program.start files, and you have to run build_css.py afterwards as well.

Two numbers define the address space. By default there is 0x600 bytes, which the README calls 1.5kB, of memory, and this can be increased in build_css.py as necessary. The program gets loaded at memory address 0x100.

I/O is done through custom I/O addresses, and the example program in the README shows exactly how. It declares function pointers for writeChar1, writeChar4 and writeChar8, plus readInput, and assigns them to fixed addresses.

c
    void (*writeChar1)(char);
    void (*writeChar4)(const char[4]);
    void (*writeChar8)(const char[8]);
    char (*readInput)(void);

The same example assigns those pointers to 0x2000, 0x2002, 0x2004 and 0x2006, and uses a separate address, 0x2100, for a numeric keyboard flag that the CSS side reads to decide which keyboard to display. That is the whole interface contract between your C code and the stylesheet: fixed memory addresses and a flag telling the page what to show.

The build works on both Linux and WSL 1 and 2, and the author notes it has not been tried on macOS.

What is missing: instruction coverage and flag behaviour

The README has a Compatibility section, and it is honest to the point of being useful.

The project implements most of the x86 architecture, but not literally every single instruction and quirk, because a lot of it is unnecessary and not worth adding. The author also says there is some behaviour that is wrong, and that some things are missing, giving the example of setting the CF and OF flag bits, and adds that this is okay.

That last point deserves emphasis for anyone learning from the code. CF and OF are the carry and overflow flags, which is where conditional branches on unsigned and signed comparison live. A program whose branches depend on carry or overflow will not behave as it would on real hardware, and the failure will be subtle rather than a crash.

The same philosophy applies to the instruction set itself. Because implementation was driven by what the author's own C programs required, the coverage is uneven in a way that is invisible until you compile something that needs an instruction that is not there. The reference sources the project leaned on give a sense of the effort: Jane Ori for the original CPU Hack, Soo-Young Lee for the 8086 instruction set reference, mlsite.net for the 8086 opcode map, and tkchia for gcc-ia16.

So the correct way to treat x86CSS is as an implementation you can read and extend, not as an emulator you can trust for general work.

Where x86CSS is the wrong tool

The author answers the practical question directly. Asked whether this is practical, the README says not really, and adds that you can get way better performance by writing code in CSS directly rather than emulating an entire archaic CPU architecture. The follow-up line is that it is fun though, and computers are made for art and fun.

That is not false modesty, it is an accurate cost model. Every instruction of your program is a change in the DOM, and the browser has to restyle and repaint for each one. A CSS animation is running the clock, so the resolution of time in the program is tied to the animation frame rate, and the whole thing runs at the speed of a stylesheet engine rather than a CPU.

So the wrong-tool cases are clear. This is not a place to run a real workload, and the memory ceiling of 0x600 bytes by default makes that obvious. It is not a teaching aid for x86 semantics, because of the missing flag behaviour. And it is not going to work outside Chromium-based browsers, which rules out any deployment where you do not control the browser.

It is also worth being clear about the licence. GPL-3.0 is a copyleft licence, so if you take the stylesheet or the build scripts into a project of your own, the terms of that licence travel with them. That is the standard trade for a licence that lets you study, modify and redistribute the work, and it is a fair one for a project of this kind.

The realistic uses are demonstration, teaching and art. The author says a blog post explaining how it works is planned, with the tricks used, and the project credits the people who inspired the approach along the way.

Editorial conclusion

Adopt x86CSS if you want to understand how a CPU can be driven by a stylesheet, or if you want a concrete artefact to build a talk or a blog post around, since it is a working implementation rather than a concept. Do not adopt it for anything that needs to compute quickly, because the author says plainly that writing code in CSS directly is far faster than emulating an archaic CPU architecture, and do not use it as a reference for x86 semantics, since CF and OF flag behaviour is described as wrong and only the instructions the author happened to need are implemented. Before building your own program, check the default memory budget of 0x600 bytes in build_css.py and confirm the address of your _start() function in program.start, because exceeding either produces a program that cannot load.

Frequently asked questions

Does x86CSS really run 8086 machine code without JavaScript?

Yes, the README states the demo is CSS-only and that a C program compiled with GCC into native 8086 machine code is executed fully within CSS. A script tag exists on the site only to provide a faster, more stable clock, and there is a JS-less clock implementation as well.

Which browsers does x86CSS work in?

The demo currently works only in Chromium-based browsers. The README notes that in Firefox it is possible to load CSS with no HTML, but that is not what the demo targets.

How do I build my own program for the x86CSS emulator?

For assembly, put the code in program.bin and the numeric address of the _start() function in program.start, then run build_css.py with Python 3 and the output goes to x86css.html. For C code, use gcc-ia16 with build_c.py, which also creates the program.bin and program.start files.

How much memory does the x86CSS emulator have?

By default there is 0x600 bytes, which the README calls 1.5kB, of memory, and it can be increased in build_css.py. The program is loaded at memory address 0x100.

Is the x86CSS emulator a complete x86 implementation?

No. The README says it implements most of the x86 architecture but not every instruction and quirk, and that some behaviour is wrong and some things are missing, such as setting the CF and OF flag bits.

What licence is x86CSS released under?

The project is licensed GPL-3.0, according to the repository LICENSE file and the project metadata.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. Project website
  4. README
  5. rebane2001/x86CSS on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rebane2001-x86css.svg)](https://hysenlabs.com/projects/rebane2001-x86css)

Was this useful?