Skip to main content
← tree
CallExpressioncode-as-consciousness
code_as_consciousness("philosophy", "advaita", "consciousness")
20247 min read

The parallels between Advaita Vedanta's model of consciousness and how programs execute.

The Runtime Beneath

Every program has three layers. There is the runtime — the substrate that makes execution possible. There is the program — the structured logic that differentiates one behavior from another. And there is the observer — the entity that watches execution, decides what matters, and derives meaning from the output.

In Advaita Vedanta, one of the oldest philosophical traditions in human thought, these three layers have names: Brahman, Maya, and Atman.

Brahman is pure, undifferentiated consciousness — the ground of all being. It does not think. It does not compute. It simply is, and in being, it enables everything that appears to happen.

Maya is the manifest world — structured, differentiated, governed by laws. It is the program that runs on top of Brahman. Maya is not illusion in the way the word is usually misunderstood. Maya is construction — the differentiation of the undifferentiated into forms, names, and behaviors.

Atman is the observer — the self that watches. In Vedantic philosophy, the deepest insight is that Atman is Brahman. The observer and the runtime are the same thing, viewed from different angles.

I did not set out to find this framework in software. I keep arriving at it.

A Runtime Does Not Know Its Programs

Consider the JavaScript V8 engine. It provides a heap, a call stack, a garbage collector, an event loop. It knows nothing about your application. It has never heard of your business logic. It cannot distinguish between a to-do app and a neural network — it executes both with identical indifference.

// The runtime does not discriminate.
// It enables all programs equally.
const runtime = {
  heap: new ArrayBuffer(1024 * 1024),
  callStack: [],
  execute(instruction: Instruction): void {
    // Pure potential — no opinion about what runs
    this.callStack.push(instruction);
    instruction.eval(this.heap);
    this.callStack.pop();
  }
};

This is Brahman. Pure potential. The enabling substrate that makes all differentiation possible while itself remaining undifferentiated.

When I design systems, I look for this layer first. What is the invariant? What is the thing that does not change regardless of what runs on top of it? In a web framework, it is the request-response cycle. In a compiler, it is the AST walker. In a database, it is the storage engine.

The invariant substrate is always the most boring part of the system and the most important.

The Program Is Not Illusion — It Is Differentiation

Maya, in popular culture, gets translated as "illusion." This is a shallow reading. Maya is not fake. The program running on V8 is not fake. It does real things — it moves money, serves pages, trains models. Maya is better understood as differentiation: the process by which the undifferentiated becomes structured.

// Maya: differentiation from the undifferentiated
type Expression =
  | { kind: "literal"; value: number }
  | { kind: "binary"; op: "+" | "-" | "*"; left: Expression; right: Expression }
  | { kind: "call"; fn: string; args: Expression[] };
 
// From a single type, infinite programs emerge.
// The grammar is finite. The programs are not.

A grammar is finite. The programs it generates are infinite. From 13 AST node types, this entire portfolio unfolds. That is Maya — the principle by which finite rules produce infinite variety.

This is why I build compilers. A compiler is the machinery of Maya made explicit. The lexer differentiates characters into tokens. The parser differentiates tokens into structure. The evaluator differentiates structure into meaning. Each stage takes the undifferentiated and gives it form.

When I architect software, I ask: what is the grammar of this domain? What are its primitives? What are its rules of composition? If I can answer those questions, I can build anything in that domain. If I cannot, I do not yet understand it well enough.

The Observer Changes Everything

In quantum mechanics, observation collapses the wave function. In software, observation is even more literal: a debugger changes the behavior of the program it observes. A console.log alters timing. A profiler changes memory patterns. The act of watching is never neutral.

Atman — the observer — is the part I find most interesting. In Vedantic philosophy, the observer is not separate from what it observes. The debugger and the runtime are, at the deepest level, made of the same stuff.

When I write code, I am the observer. I inspect variables, set breakpoints, trace execution paths. But I am also the runtime — my brain is the substrate on which the mental model of the program executes. And I am the program — my thought patterns are the structured differentiation that produces understanding.

// The observer, observing itself
function introspect(mind: Runtime): Observation {
  // To understand the program, I run it in my head.
  // To understand my head, I would need a bigger head.
  // This is the hard problem of consciousness,
  // reframed as a stack overflow.
  return mind.execute(() => mind.observe(mind));
  // RecursionError: maximum call depth exceeded
}

The hard problem of consciousness is, in computational terms, a stack overflow. The observer trying to observe itself with the same apparatus it uses to observe everything else. You cannot debug the debugger with itself.

Architecture as Philosophy

This framework is not something I apply self-consciously when I sit down to code. It is more like a lens that has slowly ground itself into shape over years of thinking about both software and consciousness.

When I design a system, I look for three things:

The substrate: What enables this system without being part of its logic? What would survive if every feature were removed? This is the runtime layer — keep it clean, keep it stable, do not let application concerns leak into it.

The differentiation: What are the rules that produce variety? What is the grammar? How do finite primitives compose into infinite behaviors? This is the program layer — make the grammar explicit, make the compositions composable, make the rules learnable.

The observation point: Who is watching, and what do they need to see? What information matters? What can be safely ignored? This is the observer layer — build for the human who will read this code, debug this system, extend this architecture.

Every well-designed system I have encountered — React's reconciler, Unix's file descriptor abstraction, the TCP/IP stack — embodies these three layers whether its creators intended it or not.

This Portfolio Is the Argument

You are reading this essay inside an AST. The nodes you traverse are the differentiated program. The traversal state you accumulate is the observation. And underneath it all, there is a Next.js runtime that knows nothing about philosophy or portfolios — it just serves pages.

Brahman. Maya. Atman. Runtime. Program. Observer.

The medium is the message. The portfolio is not about the idea that code and consciousness share a structure. The portfolio is that idea, made navigable.

I do not know whether consciousness is computational. I do not know whether computation is conscious. But I know that the same three-layer pattern — substrate, differentiation, observation — keeps appearing in both domains. And I have learned to trust patterns that keep appearing.

The tree awaits your traversal. What you find depends on where you look. That, too, is the point.