A CV viewer for Windows, written in machine code.
The point of the project was to find out how far an AI model can get generating a complete application directly in machine code. It was written by Claude (Claude Code, Anthropic) on instruction, one instruction and one byte at a time.
my-cv.exe is a PE32+ (x64) image whose every instruction was encoded
from the opcode tables, and whose every header — MZ header, 16-bit DOS
stub, COFF header, optional header, section table, import directory —
was emitted byte by byte. There is no C runtime, no framework, and no
dependency beyond five Microsoft DLLs.
Where the line actually falls. No compiler translated a source
language into instructions, and nothing of Python survives into the
executable. But Python is doing real work at build time: asm.py is an
assembler and pe.py is a linker, both small and both written as part
of this project rather than taken off the shelf. The model chose the
opcodes, addressing modes, encodings, register allocation, calling
convention and image layout; Python appended bytes, resolved addresses
and unrolled repetition. Calling that "no assembler" would be
overclaiming, so: no external toolchain. The strict version — byte
literals, no label resolution — is doable, but the interesting question
was whether the model could do the encoding, not whether it could type
numbers.
my-cv.exe 30,720 bytes
code 12,775 bytes = 2,915 hand-encoded instructions
data 13,680 bytes (strings, tables, sine table, embedded cv.txt)
bss 1,303,200 bytes (zero-filled by the loader)
It renders cv.txt as a printed-resume page: photo header, blue serif
section rules, timeline entries with right-aligned dates, chips,
word-wrapped body text — over a drifting flow-field line pattern that
parts around the pointer.
Double-click my-cv.exe, or:
.\my-cv.exe
It reads cv.txt and the portrait from the folder the exe lives in (not
the working directory), so it can be launched from anywhere. If cv.txt
is missing, it falls back to a copy embedded in the image — press E
to write that copy back out as a starting template.
| key | action |
|---|---|
1–9 |
scroll to a section (eased) |
↑ ↓, PgUp PgDn, Home End, wheel |
scroll (eased) |
R |
reload cv.txt from disk, replaying the intro |
E |
write the built-in template to cv.txt (never overwrites) |
O |
open cv.txt in your editor |
A |
replay the intro animation |
| move the pointer | the background lines part around it |
Esc |
quit |
Edit cv.txt, save, press R, and the window re-parses, re-lays out and
re-animates the document.
The pattern behind the page is a flow field, traced live every frame.
Each line is a streamline: a point is dropped off the left edge and walked across the window, and at every step its direction is read from a field
dir(x, y, t) = Σ ampᵢ · sin((x·fxᵢ + y·fyᵢ)/256 + t >> shiftᵢ)
Three waves are summed. One is not enough: a single plane wave has straight-line attractors — the direction along which its phase never changes — and every streamline collapses onto one, which is exactly what the first version did. Three waves at different angles, wavelengths and speeds keep it organic. A line stops as soon as it leaves the frame, and the whole field drifts slowly, so the pattern is never quite the same.
The points are drawn with GDI+: GdipDrawCurveI fits a cardinal
spline through them and SmoothingModeAntiAlias renders it soft, so the
integration can take long strides and still read as a smooth curve. GDI+
was already linked (it decodes the portrait), GpPoint is {INT, INT}
so the existing point buffer is passed straight in, and the only float in
the whole program is the pen width — a build-time constant loaded into
xmm1 by the one SSE instruction the encoder needs for it.
The window is built in three passes, bottom to top:
- the flat background — grey margins, the white sheet, its two border rules
- the line pattern, drawn over it
- the document — text, photo, rules, chips, timeline — on top
The third pass is the interesting one, because GDI has no alpha to composite
with. The document is rendered into its own layer on white, and that
layer is ANDed into the composite: white leaves the destination alone, so
the lines show through wherever there is no content, and anything darker
wins where there is. Antialiased glyph edges land between the two, which is
close enough that it is invisible at these line intensities.
Two things are opaque rather than blended — the photo and the status strip — so after the AND their rectangles are copied straight over from the content layer. The photo's rect is recorded while the layer is drawn and clamped to the viewport, since it scrolls.
The AND is done by hand, sixteen bytes at a time with SSE2. GDI will do
it with a SRCAND BitBlt, but that measured four times slower per byte
than SRCCOPY: with identical passes, swapping only the raster op moved
the frame from 21.5 % of a core to 53.2 %. A movdqu/pand/movdqu loop
does the same work at 17.6 %, and SSE2 is baseline on every x86-64 CPU, so
unlike a wider vectorisation it needs no feature test and no fallback path.
A 15 ms WM_TIMER drives everything:
- Eased scrolling. Keys and the wheel move a target; each tick the current scroll closes a quarter of the remaining distance, so every jump and every wheel notch glides instead of snapping.
- A staggered intro. Each row — the header, every section heading,
every paragraph, bullet and chip — has an index. A rising
introcounter minusindex × stagger, clamped to 0…256, gives that row its alpha; text colour is interpolated toward the page white and the row is drawn a few pixels low, so the page fades and slides in from the top down. Section rules and the header rule grow out to full width on the same curve. - The drifting field, above.
Because the background always has something new to show, the window cannot simply idle. The clock runs at 60 Hz and that is the frame ceiling: every tick while the pointer is moving or anything else is animating, every fourth tick otherwise.
The layer split is what makes 60 fps affordable. The document is rendered into its own layer and only re-rendered when something about it actually changed: a scroll, a resize, a reload, or a tick of the intro. The background and the compositing run every frame against that cached layer, so animating the pattern costs nothing in text layout, wrapping or glyph rasterisation.
Every layer is a DIB section rather than a compatible bitmap. That is not a detail: GDI+ rasterising into a device-dependent bitmap goes through a read-back path that measured nine times slower — 45 % of a core at 10 fps, against 5 % once the same drawing went into a DIB. The SSE2 composite needs the raw pixel pointer anyway.
Measured, at 60 fps with the pointer in motion: 17.6 % of one core. Idle at 15 fps with the pointer away from the window: 6.2 %.
UTF-8, markdown-ish, line based:
@name Falko Schwabe header fields (name and role are upper-cased)
@role Senior Developer
@photo photo.jpg jpg/png/bmp, loaded via GDI+, centre-cropped
@contact you@example.com up to eight, flowed with separators
@disclaimer A note. fine print under the contact row
@title my-cv — Falko the window title
# Erfahrung a section: blue serif heading + rule
## Senior Developer a role — opens a timeline entry (dot + rule)
### Firma, Berlin the line under it
> 03/2020 – heute a date: lifted up, right-aligned beside the role
- A bullet. long ones wrap with a hanging indent
a continuation line two leading spaces, hangs under the bullet
+ A chip rounded outline pill
`monospaced line
--- horizontal rule
Blank lines are vertical space. Anything else is body text.
python app.py
Requires only Python 3 (as the tool that emits the bytes — nothing from Python ends up in the executable).
app.py emits one contiguous virtual blob, splits it at a page boundary
into .text (r-x) and .data (rw-), resolves every label, and writes
the image. Relocations are stripped and the image base is fixed at
0x140000000, so data pointers are baked in as absolute addresses.
Startup. SetProcessDPIAware, read LOGPIXELSX from a screen DC,
then scale every layout constant and style-table field in place by
dpi/96. Create ten fonts, nine brushes, two GDI pens and two GDI+ pens,
start GDI+. Build
<exe dir>\cv.txt, read it, MultiByteToWideChar it from UTF-8, parse
it, and load the portrait. Size the window from the DPI, clamp it to the
screen, centre it, start the animation timer, and run a plain
GetMessage/DispatchMessage loop.
Parsing. A single pass over the wide buffer. Each line is
NUL-terminated in place, classified by its first character, and appended
to a flat item array ({pointer, length, style, measured height, width it was measured at}, 32 bytes) grouped into
sections ({name, length, first item, count, document offset}, 32
bytes). Bullet markers are rewritten in place from - to •; headings
are upper-cased with CharUpperBuffW.
Layout and painting. WM_PAINT renders into a cached memory DC and
blits it in one go, so there is no flicker. The body loop walks every section and
item, indexes a 64-byte-per-style table (font, colour, indent, line
height, gaps, flags, hanging indent, letter tracking), opens a timeline
entry on each ## (drawing the dot with Ellipse and closing the entry's
vertical rule when the next one starts), looks ahead for the entry's date
line to set it right-aligned beside the heading, and hands every text run
to the word wrapper, which measures with GetTextExtentExPointW, walks
back to the last space, draws the line, and applies the hanging indent to
what follows. The page is clipped with IntersectClipRect; the scrollbar
thumb is sized and positioned from the measured content height.
Calling convention. Microsoft x64 throughout. Every function uses one
uniform frame — eight pushes plus sub rsp, 0x88 — which keeps the stack
16-byte aligned at every call and leaves room for shadow space and
outgoing arguments, so incoming stack arguments sit at fixed offsets
[rsp+0xF0] and [rsp+0xF8].
The binary is unsigned, so SmartScreen may warn the first time it runs.
It touches exactly two things on disk: it reads cv.txt and photo.jpg,
and on E it creates cv.txt with CREATE_NEW — it will never
overwrite an existing one.
