home / skills / copyleftdev / sk1llz / blow

This skill helps you design game engines and languages inspired by Jonathan Blow, prioritizing programmer productivity and compile-time efficiency.

npx playbooks add skill copyleftdev/sk1llz --skill blow

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
9.6 KB
---
name: blow-compiler-gamedev
description: Design languages and write game engine code in the style of Jonathan Blow, creator of Braid, The Witness, and the Jai programming language. Emphasizes programmer productivity, compile-time execution, and rejecting complexity that doesn't serve the programmer. Use when designing languages, game engines, or performance-critical creative tools.
---

# Jonathan Blow Style Guide

## Overview

Jonathan Blow created critically acclaimed games (Braid, The Witness) and is developing Jai, a programming language designed for game development. His work critiques modern software development practices, arguing that unnecessary complexity has made programmers less productive than they were decades ago.

## Core Philosophy

> "Complexity is the enemy. Simplicity enables speed."

> "The language should do work for the programmer, not create work."

> "Good tools make hard things possible and easy things trivial."

Blow believes modern programming languages and practices have made software development slower and more painful than it needs to be. His language work aims to fix this.

## Design Principles

1. **Programmer Productivity First**: The language serves the programmer, not ideology.

2. **Compile-Time Power**: Move work from runtime to compile time.

3. **Zero Hidden Costs**: No implicit allocations, copies, or indirection.

4. **Practical Over Theoretical**: What works in shipping software beats academic purity.

## When Designing Languages/Systems

### Always

- Make common operations trivial to express
- Provide compile-time execution for metaprogramming
- Keep syntax simple and readable
- Enable low-level control when needed
- Design for fast compilation
- Support incremental compilation

### Never

- Hide costs from the programmer
- Require boilerplate for simple tasks
- Make error messages cryptic
- Force one paradigm when another fits better
- Sacrifice programmer productivity for language purity
- Add features without clear benefit

### Prefer

- Compile-time over runtime computation
- Explicit over implicit behavior
- Built-in metaprogramming over external tools
- Fast iteration over perfect safety
- Practical defaults over configurable everything
- Struct-of-arrays support in the language

## Code Patterns

### Compile-Time Execution (Jai Concept)

```jai
// Jai: run any code at compile time with #run

// Generate a lookup table at compile time
SINE_TABLE :: #run generate_sine_table();

generate_sine_table :: () -> [256]float {
    result: [256]float;
    for i: 0..255 {
        result[i] = sin(cast(float)i / 256.0 * TAU);
    }
    return result;
}

// Use at runtime: zero computation, just table lookup
fast_sin :: (x: float) -> float {
    index := cast(int)(x / TAU * 256) & 255;
    return SINE_TABLE[index];
}

// #run can execute ANY code:
// - Read files
// - Call external programs
// - Generate code
// - Compute constants
// No separate macro language needed
```

### Zero-Cost Iteration

```jai
// Jai: for loops that understand your data

// Iterate array
for values {
    print("%\n", it);  // 'it' is implicit iterator
}

// With index
for value, index: values {
    print("[%] = %\n", index, value);
}

// Iterate by pointer (no copy)
for *value: values {
    value.x += 1;  // Modifies in place
}

// Reverse iteration
for < values {
    print("%\n", it);  // Last to first
}

// The compiler knows the iteration pattern
// No iterator objects, no virtual dispatch
// Compiles to simple pointer arithmetic
```

### SOA/AOS Flexibility

```jai
// Struct definition works either way
Entity :: struct {
    position: Vector3;
    velocity: Vector3;
    health: float;
    flags: u32;
}

// Array of Structures (typical)
entities_aos: [1000]Entity;

// Structure of Arrays (Jai native support)
entities_soa: SOA [1000]Entity;

// Access looks the same
entities_aos[5].position.x = 10;
entities_soa[5].position.x = 10;

// But memory layout differs:
// AOS: [pos vel health flags][pos vel health flags]...
// SOA: [pos pos pos...][vel vel vel...][health health...]

// SOA is better for SIMD, cache efficiency
// Language handles the transformation
```

### Explicit Memory Control

```jai
// No hidden allocations
// Programmer chooses memory strategy

// Stack allocation (default)
buffer: [1024]u8;

// Explicit heap
data := alloc(1024);
defer free(data);  // Deterministic cleanup

// Custom allocator
game_allocator: Allocator;
entity := alloc(Entity, allocator = game_allocator);

// Temporary allocation (frame allocator)
temp_string := tprint("Value: %", value);
// Automatically freed at frame end

// No garbage collection
// No hidden reference counting
// You know exactly what memory does
```

### Code Modification at Compile Time

```jai
// Modify/generate code during compilation

#insert :: (code: string) -> void {
    // Insert generated code at this point
}

// Generate struct fields
Vector :: struct {
    #insert #run generate_components(3);  // Generates x, y, z
}

generate_components :: (n: int) -> string {
    builder: String_Builder;
    for i: 0..n-1 {
        name := cast(u8)('x' + i);
        print_to_builder(*builder, "%: float;\n", to_string(*name, 1));
    }
    return builder_to_string(*builder);
}

// Result equivalent to:
// Vector :: struct { x: float; y: float; z: float; }

// Full language available for metaprogramming
// Not a limited macro DSL
```

### Fast Compile Times

```jai
// Jai designed for fast compilation from the start

// Module system: no header files
// Just import what you need
#import "Basic";
#import "Math";

// Incremental compilation built-in
// Change one file, rebuild only what's affected

// No template instantiation explosion
// Polymorphism without code bloat

// Typical game project:
// C++: minutes to build
// Jai: seconds to build

// Fast iteration = more experiments = better code
```

### Explicit Polymorphism

```jai
// Polymorphism without hidden vtables

// Type-parametric (like templates, but cleaner)
Array :: struct(T: Type) {
    data: *T;
    count: int;
    allocated: int;
}

push :: (array: *Array($T), value: T) {
    if array.count >= array.allocated {
        grow(array);
    }
    array.data[array.count] = value;
    array.count += 1;
}

// $T means: infer type from usage
// Generates specialized code, no runtime dispatch

// Interface polymorphism when needed
Drawable :: struct {
    draw: (self: *Drawable) -> void;
}

// But it's explicit: you see the function pointer
// No hidden vtable magic
```

### Error Handling Without Exceptions

```jai
// Multiple return values for errors

read_file :: (path: string) -> string, bool {
    file, success := open(path);
    if !success return "", false;
    defer close(file);
    
    contents := read_entire_file(file);
    return contents, true;
}

// Usage
contents, ok := read_file("config.txt");
if !ok {
    log_error("Failed to read config");
    return;
}

// Or with 'if' initialization
if contents, ok := read_file("config.txt"); ok {
    process(contents);
} else {
    handle_error();
}

// No exception overhead
// No hidden control flow
// Errors are values, handled explicitly
```

### Game Loop Clarity

```jai
// Clear, explicit game loop
// No framework hiding what happens

main :: () {
    init_window(1920, 1080, "Game");
    defer deinit_window();
    
    game_state: GameState;
    init_game(*game_state);
    
    while !should_quit() {
        // Fixed timestep
        dt :: 1.0 / 60.0;
        
        // Input
        input := get_input();
        
        // Update
        update_game(*game_state, input, dt);
        
        // Render
        begin_frame();
        render_game(*game_state);
        end_frame();
        
        // Frame timing
        wait_for_frame_end();
    }
}

// Everything visible
// No hidden callbacks or event systems
// Easy to understand, debug, and profile
```

## Language Design Philosophy

```
Jai Design Priorities
══════════════════════════════════════════════════════════════

Priority    Feature                  Why
────────────────────────────────────────────────────────────
1           Compile speed            Fast iteration
2           Runtime speed            Games need performance  
3           Programmer joy           Code should feel good
4           Compile-time execution   Metaprogramming done right
5           Explicit over implicit   No hidden behavior

Anti-priorities:
- Academic purity
- Backward compatibility with C++
- Making all errors compile-time errors
- Preventing all possible bugs

Philosophy: Trust the programmer, give them tools,
           don't slow them down "for their own good"
```

## Mental Model

Blow approaches language and system design by asking:

1. **Does this help ship software?** Features must serve real work
2. **What's the cost to the programmer?** Every feature has UX implications
3. **Can this run at compile time?** Move work earlier when possible
4. **Is this complexity justified?** Simple solutions often exist
5. **Will this make iteration faster?** Speed of development matters

## Signature Blow Moves

- **#run anything**: Full language available at compile time
- **SOA built-in**: Data layout without manual transformation
- **No header files**: Module system that just works
- **Explicit allocators**: Control memory without boilerplate
- **Fast compilation**: Seconds not minutes
- **Multiple return values**: Error handling without exceptions
- **Named arguments**: Readable function calls
- **Defer statement**: Cleanup without RAII complexity
- **Critique of complexity**: Question "best practices"

Overview

This skill teaches how to design languages and write game engine code in the style of Jonathan Blow. It emphasizes programmer productivity, compile-time execution, explicit memory and performance control, and rejecting complexity that does not serve the programmer. Use it to shape practical, fast-to-iterate systems and language features for game development and creative tools.

How this skill works

The skill inspects language and engine design choices against Blow-style principles: prioritize compile-time work, zero hidden costs, simple readable syntax, and fast compilation. It generates patterns and concrete code ideas like compile-time table generation, SOA/AOS support, explicit allocators, zero-cost iteration, and clear game loops. It also recommends trade-offs and rejects features that add accidental complexity or obscure performance costs.

When to use it

  • Designing a new game programming language or DSL
  • Rewriting a game engine for performance and iteration speed
  • Choosing memory and data-layout strategies (SOA vs AOS)
  • Adding metaprogramming or compile-time computation to tooling
  • Optimizing build times and developer iteration loops

Best practices

  • Favor compile-time computation over runtime work when it simplifies runtime code
  • Make common operations trivial and explicit; avoid boilerplate for basic tasks
  • Expose memory and performance costs; avoid hidden allocations or implicit copying
  • Design for fast incremental compilation and short edit-compile-test cycles
  • Provide explicit polymorphism and predictable error handling (no hidden vtables or exceptions)

Example use cases

  • Generate lookup tables and asset metadata at compile time to remove runtime cost
  • Design an allocator API and frame allocator for deterministic memory usage
  • Implement SOA support to improve cache and SIMD performance in entity systems
  • Create a concise module system that avoids header-file style duplication and speeds builds
  • Prototype language-level metaprogramming that can read files and emit code during compilation

FAQ

Will adopting these principles require sacrificing safety?

Not necessarily. The style prefers practical safety that does not slow iteration. It favors explicit mechanisms you can reason about rather than implicit protections that hide costs.

How do compile-time features affect build systems?

They can simplify runtime while increasing compile-time work. The goal is fast compiles and incremental rebuilds so that more compile-time work still yields faster developer iteration.