Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Foreword

From Software to Matter…

In 2002, Stephen Wolfram published A New Kind of Science, arguing that many of nature’s most complex phenomena arise not from complicated equations, but from the repeated application of remarkably simple computational rules. His work challenged the centuries-old assumption that mathematics was the only fundamental language of science, suggesting instead that programs themselves could serve as the basis for understanding reality.

Five years later, in 2007, Karl Fant published Computer Science Reconsidered. While Wolfram was exploring the computational universe from the perspective of simple programs, Fant approached computation from an entirely different direction. He questioned many of the assumptions underlying modern computer architecture and programming languages, presenting the Invocation Model and Null Convention Logic as a fundamentally different way to think about computation—not as sequences of instructions synchronized by clocks, but as networks of relationships whose behavior emerges through the propagation of information.

For nearly two decades, these two works existed largely in parallel.

One explored the idea that simple computational rules could explain the complexity of nature.

The other explored a model of computation in which information propagates naturally through asynchronous networks without imposing artificial sequential order.

Both were intellectually compelling. Both hinted at possibilities that extended well beyond their immediate applications. Yet for most programmers, they remained difficult to translate into practical systems. The hardware, software tools, compiler technology, and AI-assisted development environments needed to connect these ideas simply did not exist.

Today, at the close of 2026, that situation has changed.

Modern semiconductor manufacturing routinely produces chips containing billions of transistors positioned with nanometer precision and operating with nanosecond timing accuracy. FPGA devices provide reconfigurable hardware of extraordinary capability. Open-source synthesis tools have matured into robust engineering platforms. Equally important, AI systems have become capable collaborators, accelerating language design, compiler construction, hardware architecture, and scientific exploration.

For the first time, these developments make it practical to build programming languages that treat computation as a physical process rather than an abstract sequence of instructions.

MatterScript is an attempt to bring these threads together.

It takes seriously Wolfram’s insight that simple computational structures can describe complex physical systems. It embraces Fant’s view that propagation and causality are more fundamental than clocks and instruction streams. It combines those ideas with modern compiler technology, geometry processing, asynchronous logic, and programmable hardware to create something neither work attempted on its own: a language that unifies geometry and computation.

In MatterScript, space is no longer an implementation detail hidden inside a place-and-route tool. Time is no longer merely a clock signal distributed across a chip. Both become first-class programming constructs.

A MatterScript program describes not only what should be computed, but where it exists, how information propagates through it, and why that physical arrangement matters.

This is not simply another hardware description language, nor is it another programming language with novel syntax. It is an attempt to narrow a gap that has existed since the earliest days of computing.

Our hardware has become astonishingly faithful to physical reality.

Our programming languages have not.

That mismatch has become increasingly costly as we ask computers to simulate fluids, biological systems, electromagnetic fields, autonomous vehicles, factories, cities, and eventually the physical world itself. We manufacture computational devices with extraordinary spatial and temporal precision, yet we continue to program them using abstractions that deliberately erase those same dimensions.

MatterScript proposes a different path.

Rather than hiding space and time beneath layers of abstraction, it makes them part of the language itself.

Whether this approach ultimately represents a new direction for programming languages is for history to decide. But the convergence of ideas that once seemed isolated—computational universes, asynchronous computation, geometry-aware placement, AI-assisted engineering, and modern programmable hardware—suggests that this is an appropriate moment to ask a simple question:

What if software could finally speak the same language as the hardware on which it runs?

The chapters that follow are one possible answer.

Drafting in progress…

Introduction: The Missing Dimension in Programming

The Missing Dimension in Programming

Nearly every programming language ever invented shares the same assumption:

Computation is independent of geometry.

Variables exist nowhere. Functions execute nowhere. Messages travel instantly. Memory has no location. Time is merely an implementation detail managed by clocks.

Meanwhile semiconductor manufacturing has quietly become astonishingly precise.

Modern chips routinely manufacture billions of devices positioned within nanometers of their intended locations while switching in nanoseconds or less. Hardware has become a spatially and temporally accurate physical object.

Software has not.

MatterScript closes that gap.

For the first time, a programming language allows developers to describe computation using the same notions of geometry, locality, propagation delay, and physical arrangement that already govern the hardware beneath it. Rather than treating placement as a compiler optimization, MatterScript makes placement part of the program itself.

The result is not simply another hardware description language. It is a language for building computational systems whose software and hardware share the same model of reality.

     PART I
Rethink computation
        │
        ▼
     PART II
Computation is a property of a physical/computational universe
        │
        ▼
     PART III
Asynchronous computation changes the programming model
        │
        ▼
     PART IV
MatterScript can learn and construct models of matter
        │
        ▼
     PART V
Geometry becomes code
        │
        ▼
     PART VI
Code becomes physical systems
        │
        ▼
     PART VII
Space and time become programming primitives
        │
        ▼
    PART VIII
The language becomes a compiler/toolchain
        │
        ▼
     PART IX
The compiled system becomes a runtime
        │
        ▼
      PART X
The runtime becomes a world model
        │
        ▼
     PART XI
Multiple machines become one distributed
space-time computational system
        │
        ▼ 
     PART XII
Complete systems

Drafting in progress…

Part I: Rethinking Computation

Introduction

Before learning MatterScript syntax, we first need to unlearn several assumptions inherited from conventional programming. This section explains why today’s software abstractions eventually become bottlenecks when computation must model the physical world.

Drafting in progress…

Why Software Forgot Geometry

Programming languages are built on an assumption so fundamental that most programmers never think to question it.

Computation is independent of space.

Variables have no location.

Functions occupy no physical volume.

Messages travel instantly.

Memory appears to be equally distant from every instruction.

The compiler may decide where code ultimately executes, but the programmer rarely cares. Geometry is someone else’s problem.

This wasn’t always inevitable.

In fact, for most of human history, geometry was considered the most intuitive and trustworthy branch of mathematics.

Euclid described mathematics in terms of points, lines, surfaces, and their relationships. Newton presented his revolutionary work in Principia using geometric arguments because they reflected physical reality in a way that symbolic algebra did not. For centuries, geometry was regarded as the language through which the physical world could be understood.

Algebra occupied a different role.

It was useful, often extraordinarily powerful, but it dealt with symbols detached from any particular physical interpretation. Equations could represent almost anything—or nothing at all.

Over time, that distinction disappeared.

During the nineteenth century, mathematics gradually shifted away from geometric reasoning toward symbolic manipulation. Hilbert demonstrated that geometry itself could be expressed axiomatically as symbols obeying formal rules. What mattered was no longer the interpretation of the symbols but the consistency of the rules governing them.

At nearly the same time, mathematicians became fascinated by a different question.

Could reasoning itself be mechanized?

The Hindu-Arabic numeral system had already shown that complex arithmetic could be performed through simple mechanical procedures. Leibniz imagined a universal symbolic language in which every scientific question could be answered by calculation alone. Later, Hilbert sought a formal method that could determine the truth or falsehood of any mathematical statement.

The search ultimately failed. Gödel proved there were limits to formal systems, while Church and Turing showed that no universal mechanical procedure could decide every mathematical question.

Yet something profound emerged from that work.

To study the limits of mathematics, researchers had developed a precise notion of a mechanical computation.

That notion became the algorithm.

The electronic computer arrived at exactly the right moment.

Unlike earlier calculating machines, electronic computers were capable of carrying out enormous numbers of these mechanical procedures automatically. Many of the mathematicians investigating algorithms—including John von Neumann and Alan Turing—were directly involved in their development.

As a result, computer science inherited not only the hardware, but the mathematical framework that had been created to study effective procedures.

The algorithm became the organizing principle of programming.

That inheritance made perfect sense.

The earliest computers possessed almost no memory, extremely limited processing power, and primitive input and output devices. Programs were written to solve numerical problems one instruction at a time. Thinking of computation as a sequence of symbolic transformations was both natural and extraordinarily successful.

For seventy years, programming languages refined this idea.

Assembly organized instructions.

FORTRAN organized calculations.

C organized memory.

Object-oriented languages organized data.

Functional languages organized transformations.

Distributed systems organized communication.

But throughout every generation, one assumption remained remarkably constant:

Programs describe sequences of operations on symbols.

Geometry disappeared.

Not because it was unimportant, but because it seemed unnecessary.


Today that assumption deserves to be reconsidered.

Modern processors contain billions of transistors arranged with nanometer precision.

FPGAs expose millions of programmable logic elements whose physical placement directly influences performance.

Robots navigate three-dimensional environments.

Autonomous vehicles continuously model space.

Scientific simulations solve problems defined on meshes, lattices, and finite elements.

Machine learning increasingly depends on spatial data structures, graph relationships, and locality of computation.

In every one of these domains, geometry is not incidental.

It is the computation.

Yet our programming languages continue to describe these systems as though geometry were merely an implementation detail to be recovered later by compilers and place-and-route tools.

We routinely manufacture computational devices whose physical organization determines their behavior, while programming them in languages that pretend physical organization does not exist.

The gap between software and hardware has never been wider.


MatterScript begins with a different assumption.

Computation always happens somewhere.

Information always travels through space.

Propagation always takes time.

The geometry of a computation is not an optimization to be discovered after the program is written—it is part of the program itself.

This does not replace algorithms.

Algorithms remain an extraordinarily useful way to describe many classes of computation.

But they are not the only way.

Just as geometry and algebra eventually came to coexist within mathematics, sequential algorithms and spatial computation can coexist within programming.

MatterScript explores what programming looks like when we stop treating geometry as an implementation detail and instead make it one of the fundamental concepts of the language.


Drafting in progress…

Time is Not a Clock

Ask a programmer what time is, and they will probably reach for a datatype.

Maybe it is a Unix timestamp. Maybe it is a DateTime. Maybe it is a floating-point number representing seconds since some epoch. Maybe it is a string containing a date, a time, and a time zone.

This seems perfectly reasonable.

Until you have to make the software work.

Then time begins to reveal itself as one of the strangest and most treacherous abstractions in all of computing.

We have built an enormous body of software whose job is, in one way or another, to answer questions about time. And the history of computing is littered with the wreckage.

The first great warning was Y2K.

For decades, computer systems commonly represented years using two digits:

1967 → 67
1984 → 84
1999 → 99

It saved space. It was convenient. It was perfectly adequate for the immediate problem.

Until the century changed.

What was supposed to happen when:

99 → 00

Was that 00 the year 2000?

Or 1900?

Or an invalid value?

The computer did not know.

The computer had never known.

We had simply chosen an encoding and quietly forgotten that we had done so.

The Y2K problem was therefore not really a problem with the year 2000. It was a problem with the assumption that a particular representation of time could stand in for time itself.

And Y2K was only the beginning.

We ran out of bits

Unix gave programmers a wonderfully simple temporal abstraction: count the number of seconds since January 1, 1970.

It is hard to imagine anything more straightforward.

Time becomes a number:

0
1
2
3
4
...

Every second, increment it.

But the original Unix convention commonly stored that number in a signed 32-bit integer.

There are only so many values that can fit in 32 bits.

Eventually, the counter reaches:

2147483647

At that point, there is nowhere else to go.

On January 19, 2038, at 03:14:07 UTC, the classic 32-bit representation reaches its limit.

The next second produces an overflow.

A date in 2038 can suddenly become a date in 1901.

The problem has acquired a name: the Year 2038 problem.

Modern 64-bit systems have largely moved beyond this particular limitation, but the underlying lesson remains.

Y2K was:

We ran out of digits.

Y2038 is:

We ran out of bits.

Neither problem was caused by time itself.

We simply built a representation with a finite range and then discovered that the universe was not obligated to stop when the representation ran out.

We run out of precision

Even when the range is large enough, numbers introduce another problem: precision.

Computers routinely represent time using floating-point numbers.

Suppose an application has a timestamp:

1734567890.123456

It may look as though the number describes time with extraordinary precision.

But floating-point representations do not provide arbitrary precision merely because we write many digits.

As numbers become larger, the spacing between representable values changes. Eventually, sufficiently small differences can no longer be represented.

Now a programmer who wants to ask:

if (time >= target)

may be relying simultaneously on:

  • the precision of the numerical representation,
  • the behavior of floating-point arithmetic,
  • rounding,
  • the resolution of the underlying clock,
  • the accuracy of the oscillator,
  • and the behavior of the operating system scheduler.

The programmer thinks they are comparing two times.

They are actually comparing two approximations of measurements of physical processes.

That distinction is easy to miss.

We can’t even agree what a time means

Then there are time zones.

Consider:

2026-11-01 01:30

What instant is that?

In some places, there may be two different occurrences of 1:30 on that date because the local clock moves backward when daylight-saving time ends.

So the value isn’t necessarily enough.

We may also need:

date
time
time zone
calendar
daylight-saving rules
historical time-zone rules

And suddenly something that appeared to be a simple scalar value has become a complicated object whose meaning depends upon geography, legislation, historical convention, and the version of a database containing those rules.

The computer isn’t confused.

Our representation is confused.

The machine has been given a collection of symbols and asked to infer a physical reality from them.

Then we discover that clocks disagree

The problem becomes even more interesting when there is more than one computer.

Suppose two machines each have a perfectly functioning clock.

Machine A records:

10:00:00.001

Machine B records:

10:00:00.002

Did A’s event happen first?

Probably.

But perhaps A’s clock is slightly fast.

Perhaps B’s is slightly slow.

Perhaps the network introduced a delay.

Perhaps the machines synchronized at different moments.

Perhaps the event happened on one machine before the other but the messages describing those events arrived in the opposite order.

Now we have entered the world of distributed systems.

Computer science has developed increasingly sophisticated mechanisms for reasoning about time and causality:

  • synchronized clocks,
  • monotonic clocks,
  • NTP,
  • logical clocks,
  • Lamport timestamps,
  • vector clocks,
  • causal ordering.

All of these are useful.

But notice what has happened.

We began with a simple question:

What time is it?

And eventually arrived at:

Which event happened first, given that no machine can necessarily observe the same clock, no clock is perfectly accurate, and messages take time to travel?

Perhaps the problem isn’t that programmers are bad at handling time.

Perhaps time was never the thing we were actually representing.

A clock is not time

This distinction is easy to lose because clocks are so useful.

A clock is a physical mechanism.

A quartz crystal oscillates.

An electronic circuit counts those oscillations.

A processor can count its cycles.

An operating system can turn those counts into seconds.

A calendar can turn seconds into dates.

A time-zone database can turn dates into local civil time.

At every step, we are transforming physical processes into increasingly convenient abstractions.

But the abstraction is not the thing itself.

A clock does not contain time.

A clock contains a process whose behavior we use as a reference for ordering and measuring events.

A timestamp does not contain an instant.

It contains an encoding that we have agreed to interpret as an instant.

A Unix timestamp is not time.

It is a coordinate in a convention.

A calendar is not time.

It is a system for organizing human experience of recurring astronomical and social events.

A time zone is not time.

It is a rule for translating between a global reference and local civil convention.

These things are extraordinarily useful.

But they are not interchangeable with the physical phenomenon they describe.

We have mistaken the ruler for the distance

There is a broader pattern here.

A programmer can measure memory in bytes, but a byte is not memory.

A programmer can measure a network in packets, but a packet is not communication.

A programmer can represent an object with a pointer, but the pointer is not the object.

And a programmer can represent time with a number, but the number is not time.

The abstraction works because it hides almost everything we don’t need to know.

Until suddenly we do need to know.

Then all the hidden complexity comes rushing back.

This is why time has produced such a remarkable collection of software disasters.

We have built layers upon layers of abstractions around an assumption that seems innocuous:

Time is a number that advances.

But physical systems don’t necessarily behave like numbers.

Events happen.

Signals propagate.

Oscillators oscillate.

Matter changes state.

Information arrives.

Systems wait.

Systems respond.

Some events cause others.

Some events happen independently.

Some events cannot be ordered from the perspective of an observer.

Some events are separated by measurable physical duration.

Others are separated primarily by causality.

These are all temporal relationships, but they are not the same thing.

What happened first?

This question exposes the limitation particularly well.

A timestamp attempts to answer:

When did this happen?

But sometimes the more useful question is:

What caused what?

And sometimes the answer is neither.

Two events may be independent.

Two machines may observe them in different orders.

A signal may physically propagate from one location to another.

A computation may proceed only because a previous computation has reached a stable state.

These are different kinds of temporal relationships.

Yet conventional programming languages tend to flatten them into a small collection of concepts:

now
before
after
sleep
timeout
timestamp

Useful, certainly.

But impoverished.

The physical world contains a much richer temporal vocabulary.

The possibility of another model

This raises an uncomfortable possibility.

What if we stopped treating the clock as the fundamental temporal primitive of computation?

What if a computational system could reason about events, ordering, causality, propagation, and temporal coordinates directly?

What if time could be represented not merely as a number, but as a relationship between physical states?

What if a system could know that one piece of information follows another because the information itself propagated through the computational fabric?

What if a distributed system could refer to a deterministic digital position in a sequence rather than continually asking whether its local clock agrees with another machine?

What if hardware could be programmed to respond to a specific temporal state rather than repeatedly polling a clock until a numerical threshold had been crossed?

These questions lead somewhere very different from the conventional timer API.

They lead toward a model in which time is a property of computation rather than merely a service provided to computation.

MatterScript begins there.

It does not discard clocks.

Clocks remain useful physical references.

It does not discard timestamps.

Timestamps remain useful representations.

It does not discard operating-system time services.

They remain indispensable when software needs to interact with the world of calendars, schedules, and human time.

The point is simply that none of these things needs to be the fundamental representation of time inside the computational model.

A clock is one way of measuring temporal change.

A timestamp is one way of naming a temporal position.

A timer is one way of requesting that something happen later.

None of them is time itself.

Time is not a clock.

And once that distinction is taken seriously, a rather strange possibility appears:

perhaps computation does not need to wait for the clock to tell it when to move.

Perhaps computation can move because something happened.

And that is where the physical machine begins to become interesting again.

Drafting in progress…

The Cost of Abstraction

Abstraction is one of the great achievements of computer science.

It is also one of the great hidden expenses of civilization.

The modern programmer can write a few lines of code and cause an astonishing amount of physical machinery to act on their behalf. A programmer does not need to know the instruction set of the processor, the topology of the memory bus, the electrical characteristics of the transistors, the details of the storage controller, or the protocols by which packets move across a network.

That is the point of abstraction.

We hide the machine so that humans can work with ideas.

And it works.

The software industry has spent more than half a century building increasingly sophisticated layers between human intention and physical computation. Each layer makes the next layer easier to use.

But there is a question we rarely ask:

What does all that abstraction cost?

Not merely in software complexity.

In transistors.

In memory.

In electricity.

In bandwidth.

In heat.

In silicon.

In data centers.

In factories.

In power plants.

And ultimately, in the physical resources of civilization.

The machine beneath the machine

Consider the most trivial computation imaginable:

2 + 2

At the level of human intention, almost nothing is happening.

Two numbers are presented to a machine.

They are added.

The answer is four.

The physical operation required to perform an addition can be extraordinarily small. Digital addition can be implemented with relatively simple logic. A handful of Boolean operations can produce the result.

But that is not what happens when a programmer writes 2 + 2 in a high-level language.

The expression first exists as text.

That text must be interpreted according to the grammar of the language. It must be tokenized. Parsed. Represented internally. Compiled or interpreted. Perhaps transformed into bytecode. Perhaps passed through a virtual machine. Perhaps dispatched through a runtime. Perhaps represented as dynamically typed objects. Perhaps allocated in memory. Eventually, some sequence of machine instructions must reach a processor.

The processor itself contains instruction decoders, registers, caches, branch predictors, memory-management machinery, execution units, interconnects, and enormous amounts of control logic.

And somewhere down toward the bottom of all of this machinery, a relatively tiny collection of digital gates performs the arithmetic.

The result is:

2 + 2 = 4

The important point is not that the computer did something wrong.

It did exactly what we asked.

The question is how much machinery had to participate in order to make such a simple expression convenient for a human being.

Abstraction does not eliminate complexity

It moves complexity.

That is the great trick of abstraction.

A Python programmer does not need to understand the processor.

The processor does not need to understand Python.

Something in between has to translate.

And then something has to translate the translation.

And then something has to execute the result.

We can visualize the descent:

human intention
      ↓
source code
      ↓
language grammar
      ↓
parser
      ↓
intermediate representation
      ↓
compiler / interpreter
      ↓
runtime
      ↓
operating system
      ↓
process
      ↓
machine instructions
      ↓
processor
      ↓
instruction decoder
      ↓
registers / execution units
      ↓
digital logic
      ↓
transistors
      ↓
electrical state

Every layer is useful.

Every layer solves a real problem.

But every layer is also another transformation.

The information has to be represented.

The representation has to be interpreted.

The interpretation has to be transformed.

The transformation has to be executed.

Abstraction doesn’t make this machinery disappear.

It makes the machinery invisible to the person using it.

That distinction matters.

The abstraction tax

We can think of this as an abstraction tax.

The abstraction tax is the physical cost incurred by representing a simple intention through a more general computational system.

It is tempting to define this as a simple ratio:

useful computation divided by total computation.

But the problem is that “useful” is not always easy to define.

A general-purpose processor does not exist merely to perform one addition. Its enormous complexity provides flexibility. The operating system provides isolation, security, multitasking, portability, device management, and thousands of other capabilities. A programming language provides expressiveness and productivity.

Those things have enormous value.

So the question is not:

Why don’t we eliminate all abstraction?

That would be absurd.

The better question is:

How much physical machinery are we willing to deploy to obtain a particular level of abstraction?

And once we ask the question that way, the answer becomes measurable in principle.

The blockchain as an abstraction tax

Consider distributed agreement.

Suppose we want a collection of machines that do not trust one another to agree on a common sequence of events.

This is not a trivial problem.

We need some mechanism for determining which event came first, which history is authoritative, and how participants can agree without a central authority.

Bitcoin’s proof-of-work mechanism solves part of this problem by making computational work—and therefore physical energy—part of the cost of participating in the consensus process.

That cost is not theoretical.

The Cambridge Centre for Alternative Finance estimated Bitcoin’s annualized electricity consumption at about 138 TWh in June 2024, with its model extending to approximately 183 TWh annualized by December 2024 as network hashrate increased.

That is a civilization-scale expenditure of energy to maintain a particular computational mechanism for distributed consensus.

And it is important to be precise here.

Bitcoin’s proof-of-work does not exist merely to put transactions in order. It also provides Sybil resistance and makes rewriting history economically expensive. The enormous energy expenditure is part of the security model.

That is precisely why the example is useful.

The physical cost is not an accidental implementation detail.

The physical cost is part of the abstraction.

Ethereum provides an extraordinary counterexample.

Ethereum once used proof-of-work as well. Shortly before its transition to proof-of-stake, Ethereum’s annual electricity consumption was estimated at approximately 78 TWh. After the transition, Ethereum estimates its current annual electricity consumption at roughly 0.0026 TWh, with the Merge reducing electricity expenditure by more than 99.98%.

The underlying lesson is much larger than cryptocurrency.

Change the computational mechanism, and the physical cost of the abstraction can change by orders of magnitude.

The problem was never “blockchains are inherently expensive.”

The problem was that one particular mechanism for establishing distributed agreement was extremely expensive.

And this is precisely the sort of question MatterScript asks.

What if a problem that currently requires enormous computational machinery could instead be represented directly in the physical structure of the computation?

What if ordering could be established without repeatedly performing enormous amounts of unrelated work?

What if time itself could become a deterministic digital coordinate?

What if the hardware could recognize that coordinate directly?

The question is not whether computation should be distributed.

The question is whether we are paying an unnecessary physical price for the way we have chosen to represent distribution and agreement.

The abstraction tax is everywhere

Blockchain is an unusually visible example because its energy consumption is easy to isolate.

Most abstraction costs are not.

They are distributed throughout the computing ecosystem.

A database query passes through layers of software.

A filesystem request passes through layers of software.

A network packet passes through layers of software and hardware.

A graphical operation passes through layers of software and hardware.

A web page passes through an extraordinary chain of representations before a human sees a collection of pixels.

Each layer is reasonable.

The complete stack can be enormous.

This is why the question of efficiency cannot be reduced to:

How many CPU instructions does this program execute?

The physical cost of computation includes everything required to make the computation possible.

Memory movement.

Cache activity.

Instruction fetching.

Branch prediction.

Operating-system scheduling.

Virtualization.

Network transport.

Storage.

Cooling.

Power conversion.

Redundant infrastructure.

All of these exist somewhere below the programmer’s abstraction.

Civilization’s computer

This matters because computing is no longer a small industrial activity.

It is infrastructure.

The International Energy Agency estimated that data centers consumed approximately 415 TWh of electricity in 2024, about 1.5% of global electricity consumption. Its base-case projection puts data-center electricity consumption at approximately 945 TWh by 2030.

These numbers cannot simply be labeled “waste.”

Data centers provide enormous value.

They run financial systems, scientific computing, communications, entertainment, logistics, medical systems, businesses, government services, and increasingly artificial intelligence.

But the scale changes the question.

When computing consumes hundreds of terawatt-hours of electricity, even a small improvement in the amount of physical computation required to produce a unit of useful work becomes economically and environmentally significant.

At civilization scale, efficiency is architecture.

How much of the machine is doing useful work?

This leads to a question that is difficult to answer precisely but impossible to ignore:

How much of the world’s computational machinery is performing useful transformations of information, and how much is translating information between abstractions?

We should be careful here.

There is no defensible universal number that says “X percent of computing is abstraction overhead.”

Different workloads have radically different requirements.

An operating system may appear to be overhead when viewed from the perspective of a single arithmetic operation, but it is providing security, isolation, scheduling, hardware management, and services that are themselves useful.

A runtime may perform work that seems unnecessary for a particular calculation but enormously increases programmer productivity.

A compiler may produce millions of intermediate operations so that a human does not have to think about the physical machine.

The abstraction is buying something.

The question is:

How much?

And perhaps more importantly:

Could we buy the same capability with less physical machinery?

From instructions to useful work

Consider two machines that both produce:

4

One machine might perform the operation through a tiny dedicated circuit.

Another might execute it through a general-purpose processor running an operating system and a high-level runtime.

Another might execute the operation as part of a much larger computation on a GPU.

All three produce the same answer.

Their physical costs can be radically different.

This suggests a more interesting metric than raw performance:

physical computation per unit of useful result.

How many transistor transitions?

How much energy?

How much memory movement?

How much bandwidth?

How much silicon?

How much cooling?

How much infrastructure?

For a particular useful result?

The answer will vary enormously depending on the abstraction and the workload.

But that is precisely the point.

We have become accustomed to measuring computers primarily in terms of abstract performance:

gigahertz
gigaflops
terabytes
transactions per second
tokens per second

Perhaps we should also ask:

How much physical reality does one unit of useful computation require?

The strange economics of generality

There is a reason general-purpose computers became dominant.

Generality is extraordinarily valuable.

A specialized circuit might perform one operation extremely efficiently.

A general-purpose processor can perform millions of different operations.

A programming language can express almost anything.

An operating system can run almost anything.

A cloud platform can host almost anything.

This is the economic bargain of abstraction:

Give up physical efficiency in exchange for flexibility.

For much of computing history, that was an excellent bargain.

Transistors became cheap.

Memory became cheap.

Storage became cheap.

Processors became faster.

So we spent the surplus on abstraction.

Instead of optimizing every computation for the physical machine, we optimized the machine for the convenience of the programmer.

And that worked spectacularly well.

But the bargain is changing.

Energy is not free.

Data movement is not free.

Cooling is not free.

Semiconductors are not free.

Data centers require land, transformers, transmission capacity, generators, cooling systems, construction, maintenance, and enormous amounts of capital.

The abstraction stack may be invisible to the programmer, but its physical consequences are not invisible to civilization.

The opportunity hiding underneath the abstraction

This is where MatterScript takes a different position.

The objective is not to eliminate abstraction.

It is to move the abstraction boundary closer to the physical computation.

Instead of describing everything as instructions for a generic processor, MatterScript can describe things in terms of the structures that actually perform them.

A geometric relationship can become geometry.

A temporal relationship can become a temporal primitive.

A digital state can become a physical configuration.

A dataflow relationship can become a spatial arrangement of logic.

A deterministic sequence can become a hardware-recognizable temporal coordinate.

The programmer does not necessarily need to descend into transistor-level design.

But the language can preserve more information about the programmer’s intention as it descends toward the machine.

That is the critical distinction.

Conventional abstraction often looks like:

intention
   ↓
general representation
   ↓
more general representation
   ↓
more general representation
   ↓
generic machine

MatterScript aims for something more like:

intention
   ↓
physical relationship
   ↓
computational structure
   ↓
physical implementation

The difference is not that MatterScript has fewer abstractions.

It is that the abstractions are intended to preserve physical meaning.

Semantic compression

There is a useful way to think about this.

Suppose a programmer says:

2 + 2

The semantic content of the statement is tiny.

If the physical system requires a vast amount of machinery to interpret and execute that statement, then most of the machinery is not creating new semantic information.

It is translating.

MatterScript seeks a form of semantic compression:

Preserve as much of the programmer’s intention as possible as the computation moves toward physical realization.

The closer the representation remains to the thing being computed, the less translation may be required.

This does not mean that every computation should become a custom circuit.

It means that the language should make it possible to express when that distinction matters.

If the programmer wants a general-purpose computation, use a general-purpose processor.

If the programmer wants a geometric object, represent geometry.

If the programmer wants a deterministic hardware event, represent the temporal relationship directly.

If the programmer wants a massive numerical operation, use the appropriate accelerator.

The abstraction should match the thing.

The cost of forgetting the machine

There is a paradox at the heart of modern computing.

We became extraordinarily successful at making programmers forget that computers are physical machines.

That success allowed software to become vastly more sophisticated.

But it also created a generation of software systems in which the physical consequences of an abstraction are often invisible until they become enormous.

A programmer can allocate another object without thinking about the memory hierarchy.

A developer can make another network request without thinking about the physical infrastructure.

A service can replicate another database without thinking about the energy and hardware involved.

A distributed protocol can perform another round of consensus without thinking about the computation occurring across thousands of machines.

The abstractions are doing their job.

That is exactly why the physical cost is so easy to forget.

The question MatterScript asks

MatterScript does not claim that abstraction is bad.

Abstraction is one of the reasons civilization has computers at all.

The question is more uncomfortable:

What if some of our abstractions are costing us far more physical reality than the problems they solve require?

What if billions of gates are being activated to accomplish work that a much smaller physical structure could perform?

What if enormous quantities of memory are being moved because the representation of a problem is disconnected from the structure of the computation?

What if terawatt-hours of electricity are being consumed because we chose a computational mechanism that was convenient to general-purpose software rather than appropriate to the underlying physical problem?

What if the distance between human intention and physical realization has become unnecessarily large?

These are not merely questions of optimization.

They are questions about the architecture of computation.

Civilization has spent decades making computers easier to program.

We succeeded.

We built abstractions upon abstractions until almost no programmer needs to think about the physical machine anymore.

That achievement was extraordinary.

But abstraction has a physical cost.

Every representation requires storage.

Every translation requires computation.

Every computation requires matter and energy.

Every layer between intention and physical action has to exist somewhere.

The machine has not disappeared.

We have simply hidden it.

MatterScript begins with the proposition that perhaps we should make the machine visible again—not so that every programmer has to learn how to design transistors, but so that the language itself can preserve the physical structure of what the programmer means.

The goal is not less abstraction.

It is better abstraction.

Abstraction that understands geometry.

Abstraction that understands time.

Abstraction that understands causality.

Abstraction that understands the physical structure of computation.

And ultimately, abstraction that does not require civilization to build an enormous machine merely to hide the much smaller machine underneath it.

The question is no longer simply:

How fast can we make computers?

It is:

How much physical reality should a computation require to express an idea?

That is a question software has rarely been forced to answer.

MatterScript intends to ask it.

Drafting in progress…

Hardware Already Knows the Answer

For decades, software has been built upon abstractions that deliberately erase the physical world.

Variables have no location.

Functions occupy no physical space.

Messages travel as though distance does not exist.

Memory appears equally close to every computation.

These assumptions have served us remarkably well. They allowed programmers to think in terms of algorithms rather than transistors, making software vastly more portable and productive.

But they have also created a disconnect.

None of these assumptions are actually true inside the machine.

A processor is not a list of instructions.

An FPGA is not a collection of algorithms.

A semiconductor is a physical object.

Every transistor occupies a precise location.

Every wire has measurable length.

Every signal requires finite time to propagate.

Every gate stores energy.

Every computation is constrained by geometry.

The remarkable thing is that hardware engineers have always known this.

They have never had the luxury of pretending that space and time don’t matter.


Consider the simplest logic gate.

From the perspective of software, an AND gate computes a Boolean function.

A AND B → C

From the perspective of hardware, something quite different happens.

Electrical signals propagate along conductors.

Charge accumulates on transistors and interconnects.

Threshold voltages are crossed.

The circuit settles into a stable physical state.

That state remains available until new information arrives.

The gate is not continuously “executing” an AND operation.

It simply exists in a configuration that represents the correct answer.

This distinction may seem subtle, but it changes how we think about computation.

Software encourages us to imagine that computation is something a processor does.

Hardware reminds us that computation is often something a circuit is.

Once the inputs stabilize, the circuit itself embodies the solution. No instruction stream is required to preserve that result. The physical arrangement of transistors, wires, capacitances, and feedback paths already contains the computation.


This idea becomes even more striking when we consider asynchronous logic.

A conventional processor repeatedly asks every register in the machine whether it should update on the next clock edge.

An asynchronous circuit asks no such question.

Information simply propagates.

Each stage waits until enough information has arrived to determine its next stable state.

Nothing happens until it can happen.

Nothing happens sooner than physics allows.

The computation unfolds through causality rather than scheduling.

MatterScript embraces this same viewpoint.

A definition does not execute because a program counter reaches it.

It resolves because the required information exists.

Names become complete.

Relationships become satisfied.

Information propagates.

The computation emerges naturally from the network.


There is another lesson hardware has been teaching us for years.

Placement is computation.

Every FPGA engineer knows that moving two connected logic blocks farther apart increases propagation delay.

Every chip designer knows that wire length limits operating frequency.

Every physical implementation tool spends enormous effort deciding where computations should exist because geometry directly influences behavior.

Yet software has traditionally ignored all of this.

A compiler receives an abstract program.

Only after the program is complete does an entirely different class of software attempt to rediscover the geometry hidden within it. Place-and-route tools search for efficient layouts, balancing timing, congestion, power consumption, and routing resources.

The programmer rarely participates in that conversation.

The physical structure of the computation is treated as an optimization problem to be solved after the language has already discarded it.


MatterScript proposes that geometry should no longer be an afterthought.

If computation depends on placement, then placement belongs in the language.

If propagation delay affects behavior, then propagation belongs in the language.

If locality determines performance, then locality belongs in the language.

The physical organization of a system is not merely an implementation detail.

It is part of the computation itself.

This is why MatterScript is not simply another hardware description language.

Verilog and VHDL ultimately produce hardware that occupies physical space, but they still encourage programmers to think primarily in terms of signals, processes, and clock cycles. Geometry enters the picture only after synthesis begins.

MatterScript starts one level earlier.

It asks a different question:

What if the programmer could describe the geometry itself?

Not just the logic.

Not just the timing.

The geometry.


Perhaps the most important lesson hardware has been teaching us is one that we have largely overlooked.

A modern FPGA contains millions of programmable elements. After configuration, those elements do not merely wait for instructions to execute. Their spatial arrangement, their interconnections, and their stable electrical states collectively embody the computation. As new information arrives, the device responds continuously because its physical structure already represents the solution.

In this sense, a configured FPGA is not simply a computer running a program.

It is a physical object whose structure is the program.

MatterScript extends this idea one step further.

If we arrange computational elements so that their geometry mirrors the geometry of the physical system being modeled—a heat exchanger, a river, a bridge, a biological tissue, or an entire city—then part of the computation no longer needs to be rediscovered at runtime.

It is already present in the structure of the machine.

The hardware is no longer merely executing a model of reality.

It becomes a computational analogue of that reality.


This leads to a perspective that is fundamentally different from conventional software.

When a MatterScript compiler places a computational mesh onto an FPGA, it is doing more than optimizing for timing. It is embedding knowledge about the physical world directly into the computational fabric.

Neighborhood relationships are established before the program begins.

Distances are already encoded.

Communication paths already exist.

Propagation delays already reflect the intended geometry.

The computation does not begin from an empty canvas.

The placement itself is frozen knowledge.

Before the first token propagates, before the first sensor reading arrives, before the first physical equation is evaluated, part of the solution already exists. It exists not as data in memory or instructions in a program, but as the physical organization of the machine itself.

Signals are no longer discovering the structure of the problem.

They are moving through a structure that already understands it.

That is the central idea behind MatterScript.

Software should no longer pretend that geometry is somebody else’s problem.

For the first time, we have the hardware, the compiler technology, and the language abstractions to treat space and time as first-class programming constructs.

In doing so, we allow software to speak the same physical language that hardware has understood all along.

Drafting in progress…

Introducing Space-Time Programming

Drafting in progress…

Part II: The Computational Universe

MatterScript begins from a remarkably simple observation.

Information always exists somewhere.

Signals occupy space.

Energy requires time to move.

Every physical interaction is local.

Every computation is embedded in a physical substrate.

For most of the history of computing, these facts have been treated as implementation details. Software describes algorithms, while hardware is responsible for making those algorithms run efficiently. The physical organization of the machine is largely hidden from the programmer.

MatterScript begins from the opposite assumption.

The physical world is not merely where computation happens.

The physical world is the computation.


For centuries, science has sought to understand nature by describing it mathematically.

A falling apple is described by Newton’s laws.

A flowing river is described by the Navier-Stokes equations.

Heat is described by Fourier’s equation.

Light is described by Maxwell’s equations.

These mathematical descriptions are among humanity’s greatest intellectual achievements. They allow us to predict the behavior of extraordinarily complex systems with remarkable accuracy.

But they are not the systems themselves.

They are abstractions.

When we build software to model a physical process, we typically begin by extracting a handful of measurable properties, deriving equations that approximate their behavior, and then solving those equations numerically. Reality is first reduced to mathematics, and mathematics is then reduced to software.

For many problems, this approach has been enormously successful.

But it is not the only possible approach.


In 2002, Stephen Wolfram proposed a different way of thinking.

Rather than viewing complexity as something that requires increasingly sophisticated mathematics, A New Kind of Science suggested that extraordinary complexity can emerge from the repeated application of remarkably simple computational rules.

The implications were profound.

Perhaps nature is not solving equations at all.

Perhaps nature is computing.

A snowflake does not solve differential equations to determine how it should grow.

A forest does not optimize a mathematical function before producing another branch.

A hurricane does not integrate the Navier-Stokes equations before changing direction.

Every component simply interacts with its immediate neighbors according to local rules.

From those countless local interactions, global behavior emerges.


MatterScript takes this idea one step further.

If reality is fundamentally an unfolding computation, then perhaps our programming languages should describe computations directly rather than forcing every physical system through layers of mathematical abstraction.

Instead of asking,

“What equations govern this system?”

we begin by asking,

“What local interactions produce this behavior?”

This is more than a philosophical distinction.

It changes how we build software.

One approach attempts to describe reality.

The other attempts to recreate it.


Consider weather prediction.

A modern weather simulation divides the atmosphere into millions of cells. At every timestep, the simulation repeatedly reads enormous quantities of data from memory, computes pressure, temperature, humidity, momentum, turbulence, radiation, and dozens of other coupled physical effects, then writes the updated state back to memory before beginning the next iteration.

The mathematics is elegant.

The engineering is extraordinary.

But much of the machine’s effort is not spent performing physics.

It is spent moving data.

The computational substrate bears almost no resemblance to the atmosphere it is attempting to simulate.


Now imagine a different computational fabric.

Suppose every programmable logic element in an FPGA represented one small parcel of atmosphere.

Each logic cell stores its own temperature.

Its own pressure.

Its own humidity.

Its own momentum.

Each cell communicates only with its neighboring cells because neighboring parcels of air communicate only with neighboring parcels of air.

When heat flows through the simulated atmosphere, information propagates through the computational fabric in exactly the same local manner.

There is no processor repeatedly fetching state from memory.

There is no cache hierarchy attempting to hide memory latency.

There is no massive movement of data between storage and computation.

The atmosphere simply evolves.

The hardware itself becomes a computational analogue of the physical system.


This is not an analog computer.

Every value remains digital.

Every interaction remains deterministic.

Every computation remains reproducible.

What changes is the organization of the machine.

Instead of forcing a spatial process onto a sequential architecture, we construct a computational substrate whose geometry mirrors the geometry of the physical system itself.

The computation unfolds for exactly the same reason the physical system unfolds:

because neighboring elements continually influence one another.


This idea extends far beyond weather.

A heat exchanger.

A bridge under load.

A section of living tissue.

An electrical power grid.

A watershed.

An ecosystem.

A city.

Each can be viewed not merely as an object to be described mathematically, but as a computational universe composed of countless local interactions whose collective behavior emerges over time.

The programmer is no longer writing an algorithm that computes the state of the system.

The programmer is defining the universe in which the system is allowed to evolve.


This is why MatterScript is fundamentally different from conventional programming languages.

Traditional languages describe algorithms.

MatterScript describes computational universes.

A MatterScript program does not simply specify what should happen.

It defines the space in which computation exists.

It defines the relationships between neighboring elements.

It defines the local rules by which those elements interact.

The global behavior is not explicitly programmed.

It emerges.


Once computation is viewed this way, many familiar distinctions begin to disappear.

Simulation and execution become the same activity.

Geometry and computation become inseparable.

Data and structure become part of the same computational fabric.

Hardware ceases to be merely a platform that executes software.

It becomes the universe in which that software unfolds.

This is the central shift in perspective that MatterScript asks the reader to make.

Instead of viewing computation as a sequence of instructions acting upon data, we begin to view computation as the natural evolution of a structured universe governed by local relationships.

Once that shift occurs, geometry is no longer an implementation detail.

It becomes one of the fundamental building blocks of computation itself.

Drafting in progress…

Computation as Propagation

Most programmers learn to think of computation as a sequence of instructions.

Read a value.

Execute an operation.

Store the result.

Repeat.

Whether the language is C, Python, Java, or Rust, this mental model rarely changes. Even when multiple processors are involved, we still imagine computation as many instruction streams executing simultaneously.

MatterScript begins from a different premise.

Computation is not the execution of instructions.

Computation is the propagation of information through a network of relationships.

This may sound like a subtle distinction, but it changes almost everything about how a program is written, understood, and ultimately executed.


Consider the simplest useful MatterScript definition.

NOT[(A<>)($Q)

    Q<$A()>

    :0[1]
     1[0]
]

If you’ve never seen MatterScript before, don’t worry about every symbol just yet.

Instead, ask a single question.

How does information move?

The definition declares one input place, A<>, and one output place, $Q.

Between them is a single expression:

Q<$A()>

This statement does not perform a computation.

It creates a relationship.

It says that whatever value eventually arrives at $A will be used to form the name of another definition. That definition will return its contents, and the returned value will propagate to Q.

The two constant definitions at the end describe the behavior of the gate.

:0[1]
 1[0]

If the incoming value is 0, return 1.

If the incoming value is 1, return 0.

That is the complete definition of logical inversion.


Now imagine that the input contains the value 1.

The flow of computation looks like this:

1
│
▼
A<>
│
▼
$A()
│
▼
Q<1()>
│
▼
1[0]
│
▼
0
│
▼
Q<>

Nothing executes.

Nothing is assigned.

Nothing is overwritten.

The value simply propagates through an existing network until it reaches a stable destination.

The computation is the movement itself.


This may feel unfamiliar because conventional programming languages hide nearly every physical aspect of computation.

Consider the equivalent statement in C.

q = !a;

The syntax is wonderfully concise.

But it leaves many important questions unanswered.

Where is a physically located?

Where is q?

How far apart are they?

How long does the operation take?

How does the result travel from one location to the other?

The language doesn’t know.

Those questions belong to the compiler, the processor, the cache hierarchy, and ultimately the hardware itself.

MatterScript does not separate these concerns so completely.

Information always exists somewhere.

Propagation always requires time.

Every relationship has a physical interpretation.


Now consider a slightly larger example.

NOT($A)(OP<>)

NOT($OP)(Q<>)

A programmer raised on imperative languages naturally asks:

“Which NOT executes first?”

The answer is surprisingly simple.

Neither.

There is no first instruction.

The first NOT gate resolves only because information reaches $A.

The second NOT gate resolves only because information later reaches $OP.

The apparent ordering is not imposed by the programmer.

It is imposed by causality.

The second computation literally cannot occur until the first has produced information.

The order emerges naturally from the dependencies within the network.

MatterScript does not schedule computation.

It allows computation to unfold.


Once we begin thinking in terms of propagation rather than execution, time acquires a very different meaning.

Suppose each NOT gate requires two nanoseconds to settle.

The first inversion occurs.

Two nanoseconds later, its output becomes available.

Only then can the second gate begin resolving.

Two nanoseconds later, the final result appears.

The total propagation delay is four nanoseconds.

No clock was consulted.

No scheduler advanced to the next instruction.

Time emerges naturally from the physical behavior of the network.


Geometry becomes equally important.

Suppose the two NOT gates are adjacent on an FPGA.

The signal travels only a very short distance.

Now imagine placing them on opposite sides of the device.

The logical behavior is identical.

The Boolean function has not changed.

Yet the computation now requires additional propagation time because the signal must travel farther.

Nothing about the algorithm changed.

Only the geometry.

This is why MatterScript treats placement as part of the computation rather than an optimization to be discovered later.

The physical arrangement of the network directly influences its behavior.


There is another important idea hidden within this simple example.

Before the input ever arrives, the entire network already exists.

Every place has been created.

Every connection has been established.

Every propagation path has already been determined.

The program is complete before a single value enters the system.

The computation is latent.

The arrival of information does not create the computation.

It merely activates part of an existing computational structure.

This is a profound departure from the conventional view of software.

Imperative languages encourage us to think that a program comes to life one instruction at a time.

MatterScript encourages us to think of a program as a structured universe waiting for information to propagate through it.

Execution is not the creation of computation.

It is the unfolding of computation that already exists.


This idea should sound familiar.

In the previous chapter, we argued that a placed computational fabric already contains knowledge about the problem it is intended to solve.

Neighborhoods have already been established.

Distances have already been encoded.

Communication paths already exist.

The placement itself is frozen knowledge.

Propagation is the dynamic counterpart to that idea.

The structure represents potential.

Information reveals that potential.

One is static.

The other is dynamic.

Together they describe a completely different model of computation.

A MatterScript program is not a sequence of instructions waiting to be executed.

It is a latent computational structure waiting for information to propagate through it.


As programs grow larger, nothing fundamentally changes.

A full adder is built the same way.

A processor is built the same way.

A weather simulation is built the same way.

Each consists of places connected by relationships through which information naturally propagates according to local rules.

The scale changes.

The principle does not.

Once you begin thinking in terms of propagation rather than execution, MatterScript stops looking like an unusual programming language.

It begins to look remarkably similar to the way computation occurs in the physical world itself.

Drafting in progress…

Geometry as Information

When programmers hear the word geometry, they usually think about graphics.

Points.

Lines.

Triangles.

Meshes.

Three-dimensional models.

These are certainly geometric objects, but they are not what makes geometry fundamentally important.

Geometry is important because it encodes relationships.

Distance.

Adjacency.

Containment.

Orientation.

Neighborhood.

Accessibility.

Before a single computation begins, a geometric arrangement already answers countless questions.

Which objects are nearby?

Which objects can interact?

Which paths are shortest?

Which regions share a boundary?

Which elements can exchange energy?

The answers exist simply because of the arrangement itself.

No algorithm has yet executed.

No search has begun.

The information is already there.


Imagine standing in a city.

Without calculating anything, you already know certain facts.

The building across the street is closer than the mountain on the horizon.

The room next door is easier to reach than another building downtown.

The sidewalk connects naturally to the intersection.

The intersection connects to another street.

None of these relationships need to be computed.

The geometry itself contains the information.

It is knowledge embedded in space.


Engineers rely on this every day.

A bridge transfers forces because its beams are arranged in particular ways.

A river flows downhill because the landscape possesses a particular geometry.

A neuron communicates only with neighboring neurons because biology arranged them that way.

A crystal forms because atoms occupy energetically favorable positions.

Nothing repeatedly computes these relationships.

They simply exist.

The structure itself determines what interactions are possible.


Modern software often works very differently.

Instead of allowing geometry to express relationships naturally, we store those relationships explicitly.

Graphs.

Adjacency matrices.

Spatial indexes.

Hash maps.

Trees.

Databases.

These structures are enormously useful, but they all represent something curious.

They are attempts to reconstruct information that geometry already contains.

Consider a molecular simulation.

A conventional program stores the position of every molecule in memory. During every simulation step it asks the same questions:

Which molecules are near one another?

Which molecules can collide?

Which forces should be applied?

Entire fields of computer science have been devoted to accelerating these searches.

Spatial partitioning.

KD-trees.

Bounding volume hierarchies.

Uniform grids.

Octrees.

These are elegant and sophisticated techniques.

But they all solve the same underlying problem.

The computation has forgotten the geometry.

It must rediscover it over and over again.


MatterScript proposes a different approach.

Suppose neighboring molecules are already neighboring computations.

Suppose adjacency is encoded directly into the computational fabric.

Suppose communication paths mirror physical connectivity.

Suppose propagation delays reflect physical distance.

Now the machine no longer asks,

“Who are my neighbors?”

It already knows.

The geometry answers the question before execution begins.


This observation extends far beyond scientific simulation.

A road network already describes transportation.

A watershed already describes drainage.

An electrical circuit already describes current flow.

A biological tissue already describes cellular communication.

A city’s layout already constrains traffic, commerce, and movement.

Every physical arrangement contains information.

The arrangement itself is part of the computation.


This leads to an important observation.

Every search performed at runtime is evidence that some relationship was unknown at compile time.

When software traverses a graph, it is discovering connectivity.

When software searches a spatial index, it is discovering neighborhood.

When software consults a lookup table, it is discovering association.

When software scans a simulation looking for the next element to update, it is discovering causality.

Many of these searches are unavoidable because the relationships genuinely change over time.

But many are not.

They exist because our programming languages have no way to express those relationships as part of the program itself.

MatterScript does.


This is why geometry occupies such a central place in the language.

It is not included to support graphics.

It is not included merely to improve hardware placement.

It is included because geometry is a remarkably efficient way to represent knowledge.

When the compiler constructs a MatterScript program, it is doing more than arranging computations.

It is embedding information.

Distances become fixed.

Neighborhoods become fixed.

Communication paths become fixed.

Possible interactions become fixed.

The placement itself is frozen knowledge.

Execution does not begin from an empty machine.

It begins from a computational fabric whose structure already answers many of the questions the program would otherwise spend its time asking.


This represents a fundamental shift in how we think about programming.

Traditionally, geometry has been viewed as the place where computation happens.

MatterScript treats geometry as part of the computation itself.

A MatterScript program therefore consists of three complementary forms of information.

The structure defines what can exist.

The geometry defines what can interact.

The propagation defines what actually happens.

Together they describe more than a program.

They describe a computational universe whose organization already contains part of its own solution.

The programmer is no longer writing instructions that operate on an abstract machine.

The programmer is designing a universe whose structure, geometry, and causal relationships allow computation to emerge naturally.

Drafting in progress…

Tokens Instead of Variables

Every programming language has a fundamental unit of computation.

In C and C++, it is the variable.

In object-oriented languages, it is often the object.

In functional languages, it is the immutable value.

MatterScript begins with something even simpler.

Information itself.

This may seem like a matter of terminology, but it reflects a fundamentally different way of thinking about computation.

Variables carry a great deal of conceptual baggage.

They imply ownership.

They imply storage.

They imply memory locations.

They imply mutation.

They imply a history of old values and new values.

For decades, these ideas have shaped the way programmers think about software.

MatterScript deliberately sets them aside.


Consider this simple MatterScript invocation.

NOT($A)(Q<>)

A programmer encountering this for the first time might naturally imagine that $A is a variable.

It is not.

Instead, imagine a single piece of information arriving at the input of the NOT definition.

If that information is a 1, it propagates into the network.

The definition responds by producing a new piece of information—a 0—which continues propagating toward its destination.

Nothing has been overwritten.

Nothing has been updated.

Nothing has been assigned.

One token of information entered the network.

Another token emerged.

The computation is simply the transformation and propagation of information.


This is quite different from the way most programmers are taught to think.

Consider a familiar statement in C.

x = x + 1;

Although it appears to describe a single operation, several things actually happen.

The current value of x is read from memory.

A new value is calculated.

The previous value stored in x is discarded.

The new value replaces it.

Mutation is central to the computation.

The variable exists before the operation.

It exists after the operation.

Only its contents have changed.

MatterScript does not encourage this way of thinking.

Instead, a computation receives information and produces new information.

The previous information is not overwritten.

It simply continues to exist as part of the causal history of the computation.


This perspective is much closer to the behavior of the physical world.

A photon does not become a different photon because it entered a lens.

A water molecule does not overwrite itself when it participates in a chemical reaction.

Instead, interactions produce new states that continue propagating through the system.

MatterScript adopts this same viewpoint.

Information flows.

Information transforms.

Information combines.

Information separates.

The network responds to arriving information rather than repeatedly modifying named storage locations.


This distinction becomes even more important when we consider time.

Variables naturally encourage us to ask questions like,

“What is the current value?”

or

“What was the previous value?”

These are perfectly reasonable questions for sequential software.

But MatterScript asks a different question.

Where is the information now?

Its history is not defined by successive assignments.

Its history is defined by the path it has taken through the network.

Information is understood through propagation rather than mutation.


This leads to another important observation.

Information does not cease to participate in a computation simply because it has stopped moving.

Consider a logic gate whose inputs have stabilized.

From the perspective of an imperative language, nothing is happening.

The computation appears to be finished.

From the perspective of MatterScript, the computation is still present.

The information remains embodied within the stable state of the network.

It is waiting.

A new input may arrive a nanosecond later.

Or a year later.

Until then, the information continues to exist as part of the computational fabric.

Information at rest is still part of the computation.

This idea reflects the physical reality of digital hardware.

Every gate behaves as a tiny sample-and-hold device.

Once it reaches a stable state, that state represents potential.

It is ready to participate in the next propagation event whenever new information arrives.

Computation therefore consists not only of moving information, but also of preserving it in stable physical configurations that remain available for future interactions.


This way of thinking also changes how the compiler views a program.

A conventional compiler asks questions such as:

Where is this variable stored?

When is it read?

When is it written?

MatterScript asks a different question.

Which computations can this token of information cause?

The compiler follows causal relationships rather than variable lifetimes.

Every token has a set of possible destinations.

Every destination may activate additional computations.

The program unfolds as information propagates through an already established network of relationships.


This perspective reaches far beyond digital logic.

A token may represent a Boolean value.

It may represent a packet moving across a network.

It may represent a molecule participating in a chemical reaction.

It may represent heat flowing through a solid, water moving through a watershed, or a financial transaction moving through an economy.

The underlying computational model does not change.

Only the meaning of the information changes.

MatterScript is therefore not built around variables.

It is built around the movement of information itself.


This is one of the most important conceptual shifts in the language.

A variable describes a place where information might exist.

A token is the information.

Once that distinction becomes natural, the rest of MatterScript begins to fall into place.

Programs are no longer collections of mutable variables manipulated by sequential instructions.

They become networks through which information propagates, transforms, combines, and occasionally comes to rest, waiting for the next cause to set the computation in motion once again.

Drafting in progress…

Events Instead of Execution

For more than seventy years, programming languages have encouraged us to think of computation as the execution of instructions.

Fetch the next instruction.

Decode it.

Execute it.

Advance the program counter.

Repeat.

Whether the processor executes one instruction at a time or billions of them in parallel, the underlying question remains the same:

What instruction should execute next?

MatterScript asks a fundamentally different question.

What new information has arrived?

That single change in perspective transforms the entire model of computation.


Consider a familiar fragment of conventional software.

if(sensor > threshold)
    alarm = 1;

It appears straightforward.

Yet hidden beneath this simple statement is an assumption that has shaped almost every programming language ever created.

The processor must continually execute code to discover whether anything has changed.

Read the sensor.

Compare it with the threshold.

Nothing changed.

Read it again.

Compare it again.

Still nothing.

Repeat millions of times every second.

The machine spends much of its life asking the same question over and over:

“Has anything happened yet?”


Now consider the same relationship expressed in MatterScript.

GREATER($sensor $threshold)(TRIGGER<>)

ALARM($TRIGGER)(SIREN<>)

Notice what is missing.

There is no loop.

There is no scheduler.

There is no polling.

Nothing executes simply because time has passed.

The network remains completely still until new information arrives.

When the sensor changes, the comparison resolves.

A token propagates to TRIGGER.

That propagation immediately becomes the cause for the next computation.

ALARM resolves.

A new token propagates to SIREN.

The event itself initiates the computation.

There was never an instruction repeatedly checking whether it should begin.


This way of thinking is much closer to the behavior of the physical world.

Nature does not poll.

A neuron does not repeatedly ask whether one of its neighbors has fired.

The electrical impulse arrives.

The membrane responds.

A new impulse propagates.

A chemical reaction does not continually check whether another molecule has entered the solution.

The molecules collide.

The reaction occurs.

The products continue propagating through the system.

A tree does not wake up every millisecond to ask whether spring has arrived.

Temperature changes.

Hormones propagate.

Growth begins.

Across physics, chemistry, and biology, systems respond to events.

They do not spend their existence searching for them.


MatterScript adopts this same model.

Computation occurs because something happened.

Not because a processor reached the next instruction.

Every propagation event may enable additional computations.

Those computations produce additional events.

Those events continue propagating through the network.

The program unfolds naturally through chains of cause and effect.

Execution becomes the visible consequence of propagation.


This idea builds directly upon one of MatterScript’s central principles.

Earlier we saw that names define more than connectivity.

They define causality.

Every connection identifies not only where information can flow, but what can cause something else to happen.

Once the compiler has constructed this causal network, it already knows every possible propagation path before the program ever begins.

When an event occurs, the compiler does not need to search for work.

It already knows which computations are affected.

The event simply activates a portion of the network that already exists.


The consequences become dramatic as systems grow larger.

Imagine simulating ten billion interacting particles.

A conventional simulation often proceeds by examining every particle during every timestep.

Has anything changed?

Should this particle move?

Has this molecule reacted?

Has this cell divided?

Most of the computational effort is spent discovering where the next interesting event might occur.

MatterScript approaches the problem differently.

If a collision occurs, only the particles involved become active.

If a chemical reaction occurs, only the neighboring molecules affected by that reaction continue propagating information.

If a neuron fires, only the neurons connected to it become candidates for further computation.

Everything else remains exactly as it was.

The computation follows the causal structure of the system rather than repeatedly searching for places where computation might be needed.


This idea extends far beyond scientific simulation.

A financial transaction affects only the accounts connected to it.

A traffic accident affects only the roads connected to it.

A power outage affects only the portions of the electrical grid connected to it.

A disease spreads only through existing patterns of contact.

The world itself is organized as networks of local events producing local consequences.

MatterScript simply gives us a language capable of expressing those same relationships directly.


There is another consequence of this model that is easy to overlook.

When nothing changes, nothing happens.

The computational fabric remains in equilibrium.

Information continues to exist.

Stable states continue to exist.

The causal relationships continue to exist.

But no unnecessary computation occurs simply to verify that the world is unchanged.

The possibility of computation is always present.

Actual computation occurs only because an event has made it necessary.


This represents one of the deepest differences between MatterScript and conventional programming.

Traditional software spends much of its time asking whether anything has changed.

MatterScript already knows what can change.

The causal relationships were established when the program was compiled.

The geometry was established when the computational fabric was constructed.

The placement already embodies knowledge about the problem.

Events simply reveal which portion of that latent structure should become active next.


The result is a model of computation that feels remarkably familiar once we recognize it.

It is the model used by electrical circuits.

By chemical reactions.

By ecosystems.

By weather systems.

By living cells.

By the physical universe itself.

Computation is no longer a sequence of instructions advancing through memory.

It is a sequence of events propagating through a connected world.

MatterScript does not invent this model.

It simply allows software to participate in it.

Drafting in progress…

Time and Memory

Computation is usually described as something that happens in time.

A program starts. Instructions execute. Memory is read and written. The program continues. Eventually it produces a result.

In this model, time is largely an external coordinate. The computer exists in space, while computation proceeds along a clock.

MatterScript takes a different view.

Time can itself be part of the computational structure. A process can persist, accumulate history, and respond differently because of what has happened before. Memory, in this sense, is not necessarily a collection of values stored at addresses. It is the persistence of structure through time.

Memory is not merely information about the past. Memory is the persistence of the past into the present.

This distinction becomes fundamental when computation is understood as propagation rather than execution.


5.1 Memory Is Not Storage

The conventional computer provides an extremely useful metaphor for memory:

DATA → ADDRESS → STORAGE → RETRIEVE

Something happens. Its representation is written to a location. Later, a program retrieves that representation.

This is a spatial model of memory.

Information exists somewhere, and remembering consists of finding it.

The metaphor is so familiar that it is easy to mistake it for a definition.

A hard drive stores files. RAM stores values. A database stores records. A program retrieves them when needed.

But not every system that exhibits memory works this way.

An organism does not carry a collection of discrete files corresponding to everything that has happened to it. A learned skill is not necessarily retrieved from a particular address. A habit does not need to be opened before it can influence behavior. The history of a process can be present in the structure and behavior of the process itself.

This suggests another model:

HISTORY ─────────────────→ PRESENT
   │                          │
   └──── persistent structure ┘

The distinction is subtle but important.

In the first model, the past is stored somewhere.

In the second, the past persists as part of what the system is now.

MatterScript is concerned with the second kind of structure.


5.2 Persistence

Fant describes a process expression as:

A structure of memories of various persistences.

This provides a useful starting point for thinking about memory in a space-time computational system.

A process is not simply a thing that receives transient inputs. It is itself a locus of persistence.

Presentations flow through it. Some are extremely transient. Others persist longer. Some relationships between them persist longer still.

Fant writes:

The expression itself is a locus of persistence that is relatively stable from presentation to presentation.

This means that persistence does not have to be implemented as a separate memory subsystem.

The structure of the process can itself provide persistence.

A presentation may exist for a moment.

A perception may persist for longer.

A learned response may persist for years.

A structural characteristic of an organism may persist for its entire lifetime.

These are all different forms of temporal persistence.

A hierarchy of persistence

The organism provides an intuitive example of this hierarchy.

At the innermost level is a momentary input: something happening now.

That input may persist as a sensory event.

A collection of events may form a perception or experience.

Repeated experience can produce learned responses.

Repeated responses can become habits and dispositions.

The entire history is incorporated into the continuing structure of the organism.

The result is not a database of increasingly old records.

It is a hierarchy of structures with different persistence timescales.


5.3 Memories of Different Persistence

A conventional memory architecture tends to distinguish memory by location:

 register
    ↓
  cache
    ↓
   RAM
    ↓
  disk
    ↓
 archive

MatterScript can instead think about memory in terms of persistence:

  moment
    ↓
  event
    ↓
perception
    ↓
learned response
    ↓
  habit
    ↓
organism

The important property is not where something is stored.

The important property is how long its structure remains relevant to subsequent propagation.

This provides a more general definition of memory:

A memory is a structure whose persistence allows a process to relate what has happened to what happens next.

A memory need not be an explicit data object.

It may be:

  • a persistent token,
  • a geometric relationship,
  • a delayed association,
  • a stable boundary,
  • a learned transformation,
  • a physical configuration,
  • a recurring pathway,
  • or an entire persistent expression.

The same principle applies across different physical implementations.

A neural structure, a mechanical mechanism, a semiconductor circuit, and a MatterScript expression can all exhibit memory without requiring that memory to take the form of a conventional addressable data record.


5.4 Appreciating Differentness Through Time

MatterScript is fundamentally concerned with differentness.

A process encounters one condition, then another. The significance of the second condition can depend upon the persistence of the first.

Fant expresses this directly:

Appreciating differentness through time requires memory to associate a differentness from the past with a differentness in the future.

This is the key transition from simple persistence to temporal computation.

Consider a sequence:

A → B

Without memory, B is simply the current condition.

With persistence, the process can encounter:

A → B

and distinguish that sequence from:

C → B

The current event is the same.

The history is different.

Therefore the process can behave differently.

Memory has entered the computation.

The system does not need to retrieve a stored representation of A. It only needs some aspect of A to have persisted sufficiently to participate in the interpretation of B.

This is a fundamentally different computational model from:

READ memory[address]
COMPARE value
WRITE result

The temporal structure itself performs part of the computation.


5.5 History Becomes Point of View

Persistence has another consequence.

A sufficiently persistent structure does not merely remember the past. It begins to interpret the present through the accumulated past.

Fant describes this as the “arrogance of bulk”:

Any association expression of sufficient persistence and stride of appreciation, even if it is continually changing, becomes a de facto referent: an arrogant bulk projecting its appreciations on all it encounters.

The terminology is deliberately provocative, but the computational idea is important.

A persistent expression carries a history.

That history affects which differences it can appreciate.

Those differences affect how it responds.

Thus:

PERSISTENCE
     ↓
  HISTORY
     ↓
APPRECIATION
     ↓
POINT OF VIEW

A point of view is therefore not merely an opinion stored inside a system.

It can be understood as the consequence of persistent structure interacting with new events.

Two expressions can encounter the same event and respond differently because their histories are different.

The difference does not necessarily reside in a separately retrieved memory.

It resides in the structures that have persisted.

This is one reason biological systems are such useful examples of space-time computation. An organism carries its history in its continuing organization.


5.6 The Past Is Present

This gives us a useful distinction between two statements:

“The system remembers the past.”

and:

“The past is part of the system’s present state.”

The first suggests storage and retrieval.

The second suggests persistence.

For a sufficiently persistent process, these become nearly inseparable.

A person’s ability to recognize a familiar face is not necessarily the result of retrieving a file labeled with that face. The organism has been changed by its previous encounters.

Likewise, a learned physical mechanism does not need to remember its design as an abstract description. Its structure embodies the history that produced it.

The past has become present structure.

This can be expressed computationally as:

state(t) = history → present

rather than:

state(t) = current_input + memory[address]

The first expression is not intended as a conventional equation. It is a change in ontology.

The present is understood as the continuation of a history rather than as a state that periodically consults an external archive.


5.7 Incidental Time

Not every computation that takes time is a temporal computation.

This distinction is essential.

A conventional program may execute for ten seconds. That does not mean that the program has computationally represented ten seconds.

The passage of time may simply be an incidental consequence of execution.

Fant calls attention to this distinction in his discussion of incidental time.

An expression may extend through time simply because of implementation constraints, resource limitations, or the convenience of reusing one structure sequentially rather than constructing many structures in parallel.

A conventional computer is full of such examples.

A processor may execute:

A
B
C
D

sequentially even when the computation has no conceptual reason for A, B, C, and D to occupy successive moments.

The sequence is an implementation strategy.

MatterScript distinguishes this from a computation in which temporal relationship is itself meaningful.

Compare:

Incidental time

A → B → C → D

The system happens to perform these operations sequentially.

Computational time

A ───────→ B
    ↑
 persistence

The relationship between A and B depends upon A persisting into the interval in which B occurs.

The first is sequential execution.

The second is temporal structure.

This distinction is one of the foundations of space-time programming.


5.8 Time as Computational Structure

Traditional programming tends to make time external to structure.

A program has:

  • data,
  • operations,
  • memory,
  • addresses,
  • and a clock.

The clock determines when operations happen.

MatterScript instead permits time to participate directly in the organization of computation.

A delay can be structural.

A persistence can be structural.

A temporal relationship can be structural.

A process can distinguish an event because another event is still present, or because another event has ceased to be present, or because a persistent structure connects the current event to something that happened earlier.

In this model:

TIME
 │
 ├── persistence
 ├── delay
 ├── ordering
 ├── association
 └── memory

These are not merely runtime measurements.

They can be properties of the computational expression itself.

This is why MatterScript does not treat time simply as a clock attached to otherwise timeless computation.

Time can be part of the machinery.


5.9 Temporal Memory and Spatial Structure

Time and space provide two complementary forms of computational relationship.

A spatial relationship says:

These things are related because of where they are.

A temporal relationship says:

These things are related because of what persists between their occurrences.

Consider two structures:

SPATIAL RELATIONSHIP

A ───── B

The relationship is expressed by their arrangement.

Now consider:

TEMPORAL RELATIONSHIP

A ─────────→ B
     │
     └── A persists

The relationship is expressed by persistence through the interval.

MatterScript treats both as computationally meaningful.

This leads to a useful symmetry:

                 COMPUTATION
                      │
          ┌───────────┴───────────┐
          │                       │
        SPACE                    TIME
          │                       │
      LOCALITY               PERSISTENCE
          │                       │
    RELATIONSHIP               MEMORY
          │                       │
     PROPAGATION             APPRECIATION

Space determines what can interact through locality.

Time determines what can remain relevant through persistence.

Together they provide a richer substrate for computation than either dimension considered independently.


5.10 From Memory to MatterScript

The conventional programming model asks:

Where is the value stored?

MatterScript can ask a different question:

What persists, where does it persist, and what can that persistence affect?

This changes the programmer’s conception of state.

A variable normally represents something whose value can be changed.

A persistent expression represents something whose continued existence has computational consequences.

That distinction matters when computation is mapped onto physical systems.

A physical circuit does not need to simulate every aspect of memory as an abstract data structure. It can embody persistence directly through:

  • physical state,
  • geometry,
  • capacitance,
  • delay,
  • feedback,
  • stable configurations,
  • asynchronous handshakes,
  • or other mechanisms that preserve distinctions through time.

MatterScript’s purpose is not to hide these mechanisms behind a conventional sequential abstraction.

It is to describe computational structure in terms that remain meaningful when the computation becomes physical.


5.11 Memory Without a Memory Subsystem

This leads to a broader principle:

A system does not necessarily need a separate memory subsystem in order to have memory.

If a structure persists and that persistence affects subsequent behavior, the system already possesses a form of memory.

This can happen at many scales.

A token can persist.

A place can persist.

A geometric arrangement can persist.

A process can persist.

A biological structure can persist.

An entire world model can persist.

The implementation may change, but the computational principle remains the same.

Memory is therefore not defined by a particular technology.

It is defined by a relationship between persistence and subsequent appreciation.


5.12 From History to Computation

The resulting model is compact:

EVENT
  ↓
PERSISTENCE
  ↓
HISTORY
  ↓
APPRECIATION
  ↓
RESPONSE
  ↓
NEW EVENT

The process continuously transforms its history into its present behavior.

There is no requirement that the history be serialized into a file.

There is no requirement that the system explicitly retrieve the past.

There is no requirement that every historical state remain separately addressable.

What matters is that enough structure persists for the system to distinguish what is happening now in relation to what has happened before.

This is memory as temporal computation.


5.13 The Space-Time Programmer

The conventional programmer thinks primarily in terms of values and operations:

value → operation → value

The space-time programmer must also think in terms of:

place
persistence
propagation
locality
delay
association

The question is no longer merely:

What value should this computation produce?

It becomes:

What should persist, where should it persist, what should it encounter, and for how long should that relationship remain computationally relevant?

This is a different way of designing systems.

It is particularly important when the destination of a program is not merely another software runtime, but a physical system.

A conventional compiler translates abstract operations into instructions.

A space-time compiler can instead translate structure into relationships among physical events, places, and persistence.


5.14 The Central Principle

Fant’s treatment of time and memory gives us a concise foundation for this view:

A process can be understood as a structure of memories of different persistences.

From that perspective, memory is not fundamentally a container.

It is persistence.

And persistence allows a process to relate the past to the future.

That relationship permits the process to appreciate differentness through time.

The accumulated history of those appreciations produces behavior, disposition, and point of view.

Thus:

PERSISTENCE
     ↓
  MEMORY
     ↓
  HISTORY
     ↓
APPRECIATION
     ↓
POINT OF VIEW
     ↓
  BEHAVIOR

This is why time is not merely a clock.

A clock measures the passage of time from outside the computation.

A temporal structure uses persistence as part of the computation itself.

MatterScript treats that distinction as fundamental.

Computation does not merely happen in time. Computation can be made from time.


Relationship to the Next Chapter

Time provides one dimension of computational relationship.

Space provides another.

If persistence allows a process to maintain relevance across time, locality determines which structures can directly encounter one another in space.

The next chapter therefore turns from temporal relationship to spatial relationship:

Locality as a Programming Primitive.

Together, persistence and locality provide the foundation for computation as a physical process rather than as a sequence of abstract instructions.

Locality as a Programming Primitive

One of the deepest assumptions in modern software is that everything is, in principle, equally accessible.

A variable stored in memory can be read from anywhere.

An object may invoke methods on another object across the program.

A database query may retrieve information from anywhere in the world.

The programmer is free to describe relationships without regard for physical distance.

The machine worries about locality later.

Caches are introduced.

Memory hierarchies are optimized.

Distributed systems attempt to recover proximity after the fact.

In other words, locality is usually treated as an optimization.

MatterScript begins from the opposite assumption.

Locality is not an optimization. It is a computational primitive.


This idea has its roots in Karl Fant’s Computer Science Reconsidered, where he introduced the concept of the theng.

A theng is not simply a variable, nor is it an object in the object-oriented sense.

A variable has identity, but no place.

An object has identity and behavior, but still no inherent physical location.

A theng possesses identity, persistence, locality, and relationships.

It exists somewhere.

Its location matters.

Its neighbors matter.

Its interactions arise because of that neighborhood.

Fant described a theng as a persistence whose value may change over time.

Persistence belongs to the theng itself.

Values belong to the moment.

Different thengs become associated because they are sufficiently close for their values to interact.

The asserted values of neighboring thengs form names, and those names invoke value transformation rules.

In this way, behavior emerges naturally from local relationships rather than from globally executed procedures.


This distinction is fundamental.

MatterScript does not ask, “What should this computation do?”

It first asks,

“What can this computation interact with?”

Once locality is established, the possible interactions become known.

Those interactions define causality.

Causality determines propagation.

Propagation produces computation.

The geometry of the system is therefore not merely a representation of the computation.

It is one of its defining components.


This perspective appears repeatedly throughout the natural world.

A molecule does not interact with every other molecule in the universe.

It interacts with neighboring molecules.

A neuron communicates only with neurons connected to it.

A tree exchanges water and nutrients with the soil surrounding its roots.

A parcel of air exchanges heat, pressure, moisture, and momentum with the parcels immediately around it.

None of these systems continually search the world for possible interactions.

Their neighborhoods already define what interactions are possible.

The geometry itself constrains the computation.


This observation also lies at the heart of A New Kind of Science.

One of Stephen Wolfram’s central insights is that remarkably complex behavior can emerge from extraordinarily simple local rules.

A cellular automaton does not require a global controller.

Each cell examines only its immediate neighbors.

From those local interactions emerge structures that move, reproduce, stabilize, collide, and evolve.

Complexity arises not because individual rules are complicated, but because simple interactions are repeated across enormous collections of locally connected elements.

MatterScript provides a language capable of expressing this same principle directly.

Local interaction is not simulated on top of an imperative machine.

It is the native computational model.


Consider a weather simulation.

Traditional software often stores millions of atmospheric cells in memory.

During every timestep, the program repeatedly determines which neighboring cells exchange heat, pressure, moisture, and momentum.

Even highly optimized implementations spend considerable effort rediscovering relationships that are already implied by the geometry of the atmosphere.

MatterScript approaches the problem differently.

Each atmospheric cell already knows its neighborhood.

Propagation naturally occurs only between adjacent cells.

The simulation no longer spends its time searching for interactions.

It spends its time resolving them.

The same idea applies equally well to fluid dynamics, biological growth, electrical networks, ecosystems, and chemical reactions.


Chemistry provides perhaps the clearest example.

Atoms do not continually ask whether another compatible atom exists somewhere in the universe.

They interact with the atoms nearby.

Those local interactions produce molecules.

Those molecules interact with neighboring molecules.

Larger structures emerge from countless local events.

The universe never pauses to solve the global problem.

Global behavior emerges from local interactions.

MatterScript embraces this same philosophy.

Rather than describing an all-knowing program directing every step of the computation, it describes neighborhoods whose local interactions allow larger behavior to emerge naturally.


This changes the role of the compiler in a profound way.

The compiler is no longer arranging computations simply to improve performance.

It is constructing neighborhoods.

Those neighborhoods define which computations may interact.

Those interactions define causality.

Those causal relationships define propagation.

And propagation gives rise to the computation itself.

The resulting computational fabric mirrors the structure of the system it represents.

Whether that system is a digital circuit, a chemical reactor, a biological organism, or an evolving planetary climate, the underlying principle remains unchanged.


This represents another departure from conventional software.

Programming has traditionally been the art of describing behavior.

MatterScript is the art of describing neighborhoods.

Once those neighborhoods exist, behavior is no longer written one instruction at a time.

It emerges through local interactions unfolding across the computational fabric.

In MatterScript, locality is not an implementation detail.

It is the language in which reality writes its algorithms.

Drafting in progress…

Part III: Asynchronous Thinking

Most programmers think in clocks.

MatterScript thinks in causality.

Instead of asking “What happens every cycle?” we ask “What happens when the required information exists?”

This section introduces Null Convention Logic and Karl Fant’s Invocation Language as the conceptual foundation of MatterScript.

Drafting in progress…

Why Clocks Are Artificial

We tend to think of time as one of the most fundamental things in computing.

Every processor has a clock. Every operating system has a clock. Every programming language has some way to ask what time it is, wait for a period of time, schedule something for later, or measure how long an operation took.

It seems almost impossible to imagine computation without clocks.

But there is something strange about this picture.

A clock is not time.

A clock is a machine that imposes an ordering on physical events and gives us a convenient way to count them.

That distinction matters.

The conventional computer was built around the assumption that computation must happen in discrete steps, and that those steps must be coordinated by a common external metronome. The clock ticks. Logic changes state. The clock ticks again. Logic changes state again.

This arrangement is extraordinarily useful. It is also an abstraction.

The physical world does not actually operate this way.

There is no universal clock ticking inside the universe, telling every atom, transistor, electron, and photon when it is permitted to change state. Physical events occur because physical conditions change. A wave propagates. A molecule collides. A transistor switches. A signal arrives. A system becomes stable. Another system responds.

The clock is something we introduced to make the behavior of machines easier to organize.

MatterScript asks a different question:

What if time did not have to be imposed on computation from the outside?

The clock as a computational crutch

The conventional processor is fundamentally clock-centric.

A typical program is conceived as a sequence:

fetch
→ decode
→ execute
→ store
→ wait
→ repeat

The clock provides the cadence that keeps everything moving.

This gives us an extraordinarily simple mental model. At one instant, the machine has one state. At the next clock cycle, it has another.

But this simplicity comes at a price.

The clock must run fast enough for the slowest operation that needs to complete within a cycle. Logic that could have completed much earlier must wait. Logic that could operate independently is often forced into a common rhythm. Increasing performance frequently means increasing clock frequency, adding pipelines, adding synchronization machinery, adding prediction, or adding increasingly sophisticated mechanisms for working around the limitations of the global clock.

The clock solves the coordination problem by making everything agree to move together.

That is not the only possible solution.

Null Convention Logic begins from almost the opposite premise.

Instead of asking:

What time is it?

it asks:

Has the information required for this operation arrived?

That is a profound difference.

Time can emerge from information

In Null Convention Logic, a computational element does not necessarily need a clock to tell it when its inputs are valid.

The system can distinguish between DATA and NULL.

A collection of signals reaches a valid DATA state. The logic responds. The resulting information propagates forward. Eventually the system returns to NULL, making the computational fabric ready for the next wave of information.

The computation therefore has a temporal character without requiring an external clock to impose that temporal sequence.

The information itself moves.

The state transition itself creates the ordering.

A new symbol arriving behind an existing symbol can displace it. DATA becomes NULL. NULL becomes DATA. The transitions propagate through the geometry of the circuit.

What emerges is a wavefront.

And a wavefront is a temporal phenomenon without requiring a clock in the conventional sense.

Consider the difference.

A conventional digital system might say:

clock 1 → compute
clock 2 → compute
clock 3 → compute
clock 4 → compute

An NCL system can instead behave more like:

symbol → symbol → symbol → symbol
       →        →        →
    propagation through space

The temporal ordering is carried by the movement of information through the fabric.

This is important because it reconnects computation with physical reality.

The circuit has geometry.

The signals have position.

Propagation has duration.

Causality has direction.

Time is no longer merely a number attached to an event. It is part of the behavior of the physical system.

From clocks to wavefronts

This suggests that there are at least two fundamentally different ways to understand computational time.

The first is imposed time.

A clock says:

You may change state now.

The second is emergent time.

The physical state of the system says:

The conditions required for this transition now exist.

The distinction is easy to overlook because conventional digital computers have made clocked computation so familiar that we mistake the implementation technique for a fundamental law.

It isn’t.

A clock is one way of coordinating events.

It is not the definition of computation.

MatterScript is therefore interested in a richer temporal vocabulary.

A programmer should eventually be able to describe relationships such as:

when this arrives
when this becomes valid
after this transition
when this wave reaches here
at this digital time
at this physical time

These statements describe temporal relationships without necessarily prescribing the mechanism by which those relationships are achieved.

That mechanism might be an operating-system scheduler.

It might be a hardware timer.

It might be an interrupt.

It might be propagation through an NCL circuit.

Or it might be something that conventional software has never had a convenient way to express at all.

Digital time

This is where MatterScript’s digital blockchain technology introduces another possibility.

MKRAND produces a deterministic sequence of 128-bit digital blocks.

A block might look like:

[<:d9ca177b669ab95b6fef270657dff32c:>]

The important property is not simply that the value is large or apparently random.

It is that the sequence is ordered and reproducible.

If we have:

B₀
B₁
B₂
B₃
B₄
...

then B₄ is not merely an arbitrary value that happens to occur after B₃.

It is a specific, addressable point in the sequence.

This gives us something unusual:

time can have an address.

A MatterScript program can conceptually refer to a future temporal state rather than merely waiting for an elapsed duration.

If the current block is B, the programmer can ask for:

B + 5

The MKRAND subsystem can deterministically generate the intervening blocks and identify the fifth subsequent state.

Hardware can then be configured to respond when that state occurs.

No polling is required.

No process has to repeatedly ask whether the time has arrived.

The hardware can simply wait for the digital condition corresponding to that temporal coordinate.

This is a fundamentally different abstraction from:

sleep(5)

A sleep operation says:

Stop doing things for some approximate duration.

A digital temporal reference says:

This event belongs to this specific position in an ordered sequence of time.

That distinction becomes particularly important when computation is distributed.

Time without a central clock

Imagine two pieces of hardware separated by a large physical distance.

They each possess the same deterministic temporal sequence.

Neither needs to continuously ask the other:

Where are you in time?

Both can independently derive the same future temporal state.

If both are programmed to respond to B₁₀₀₀, then both know what event they are waiting for.

They do not have to share the event itself.

They share its coordinate in digital time.

This does not make physical simultaneity magically perfect. Real oscillators drift. Propagation takes time. Components have tolerances. Temperature changes physical behavior.

But it does provide something extremely valuable:

a common, deterministic ordering of events.

The distinction is important.

Digital time can provide exact ordering even when physical time can only be synchronized within the accuracy of the underlying physical timebase.

That gives distributed hardware a new kind of temporal reference.

Instead of continually synchronizing clocks, systems can synchronize around a deterministic sequence of digital states.

The blockchain, in this sense, is not merely a ledger.

It becomes a coordinate system for computation through time.

Connecting digital time to physical time

Of course, computers ultimately inhabit the physical universe.

A digital sequence has to correspond to something physical if we want to use it to trigger real events.

This is where the relationship between MKRAND and a crystal oscillator becomes important.

If the hardware can establish, with sufficient accuracy, how many clock cycles are required to generate or advance through a block, then the digital sequence can be associated with a physical timebase.

We can establish a relationship between:

MKRAND block
      ↓
hardware cycles
      ↓
crystal oscillator
      ↓
physical time
      ↓
wall-clock time

Now the digital coordinate does not exist in isolation.

It is anchored to physics.

The result is a hierarchy of time rather than a single notion of time.

At the highest level, a MatterScript programmer might care about wall-clock time:

Tuesday at 14:30.

At another level, the program may care about physical time:

17.25 microseconds from the reference event.

At another level, they may care about digital time:

block B₁₂₃₄₅.

And at the lowest level, they may care about a physical propagation event:

when the wavefront reaches this gate.

These are not competing definitions of time.

They are different representations of temporal relationships at different levels of the machine.

Time becomes a first-class domain

This is where MatterScript begins to depart most clearly from conventional programming languages.

Traditional programming languages tend to expose time through functions.

You call a function to get the current time.

You call another function to sleep.

You register a callback.

You configure a timer.

You wait for an interrupt.

Time exists outside the language, and the language provides mechanisms for asking the operating system or hardware about it.

MatterScript can treat time differently.

Time can become a domain of computation.

The programmer describes a temporal relationship, and the system determines how that relationship can be realized by the underlying physical substrate.

At one level:

at 14:30:
    activate pump

At another:

after 500 microseconds:
    trigger sensor

At another:

at B + 5:
    emit signal

And at another:

when wavefront reaches node:
    transform symbol

These statements appear superficially similar.

But they represent very different temporal mechanisms.

The first may require an operating-system service.

The second may use a hardware timer or interrupt.

The third may use the MKRAND temporal sequence.

The fourth may require no clock at all.

The language should not force the programmer to pretend that these are all the same thing.

They aren’t.

Space and time return together

There is a deeper connection here to geometry.

When software forgot geometry, it also forgot something about time.

Conventional software treats computation as an abstract sequence of instructions. The instructions have an order, but they have little inherent spatial meaning. Memory is an address. A processor is a generic execution engine. A program is a sequence.

MatterScript begins with a different premise.

A computation can occupy space.

Signals can have position.

Logic can have topology.

Information can propagate.

And once computation has physical extent, time becomes inseparable from that geometry.

A signal arriving at one gate before another gate is not merely a difference between two timestamps.

It is a physical relationship.

The distance, topology, propagation characteristics, and state of the computational fabric all participate in determining what happens next.

This is why geometry and time belong together in MatterScript.

Space describes where computation exists.

Time describes when relationships propagate through it.

The conventional computer separated these concepts so thoroughly that we came to think of them as separate abstractions.

Physical computation never did.

The machine that inhabits time

The ultimate goal is not to eliminate clocks.

Clocks remain useful. They provide a stable reference. They allow us to measure physical processes and coordinate systems when that is the appropriate mechanism.

The mistake is treating the clock as the only meaningful representation of computational time.

MatterScript can have clocks without being imprisoned by them.

It can use an operating-system clock when wall time matters.

It can use a crystal oscillator when physical precision matters.

It can use hardware interrupts when deterministic triggering matters.

It can use MKRAND when a reproducible digital temporal coordinate matters.

And it can use NCL propagation when the computation itself can provide its own temporal ordering.

These mechanisms do not have to compete.

They can form a hierarchy.

The programmer can describe relationships.

The compiler and hardware can choose the appropriate temporal substrate.

That is the larger idea.

A conventional program executes according to a clock.

A MatterScript system can execute according to relationships in space and time.

The difference may sound subtle.

It isn’t.

Once time becomes something that computation can represent, address, propagate, synchronize, and inhabit—not merely something it can measure—the boundary between software and physical process begins to disappear.

And that is precisely where MatterScript wants to operate.

Drafting in progress…

When Absence Becomes Information

Every programmer has encountered some version of the same problem.

A value hasn’t arrived yet.

A pointer hasn’t been initialized.

A message is still in transit.

A thread must wait for another thread to finish.

A computation cannot continue because the required information does not yet exist.

Entire programming languages and software frameworks have been built around solving this problem.

Null pointer checks.

Optional types.

Maybe<T>.

Option<T>.

Promises.

Futures.

Queues.

Mutexes.

Semaphores.

Condition variables.

Reactive streams.

Although they appear very different, they all answer the same fundamental question:

Is there valid information here yet?

For decades, we have treated this as a software problem.

It isn’t.

It is a hardware problem that software has spent half a century compensating for.


Traditional digital logic understands only two values.

Zero.

One.

A logic gate cannot distinguish between a valid piece of information and the absence of information.

It simply responds whenever its inputs change.

As a result, conventional digital systems require an external mechanism to decide when a computation has finished.

That mechanism is usually a clock.

The clock does not know whether every computation has completed.

It merely assumes that enough time has passed.

Every clock cycle is, in effect, an educated guess that the data has arrived.


Karl Fant’s NULL Convention Logic (NCL) begins with a remarkably simple observation.

What if a logic gate could recognize not only the value being presented to it, but whether valid information had been presented at all?

Instead of two logical states, the computational fabric now recognizes three conditions.

True.

False.

And NULL.

NULL does not mean zero.

It does not mean false.

It does not represent an error.

It means something much more fundamental.

There is presently no valid information here.

That single distinction changes the behavior of the entire computational system.


Rather than relying on a clock, every gate now recognizes the boundaries between valid data and the absence of data.

Computation proceeds as alternating wavefronts.

A wave of valid information propagates through the network.

When every participating gate has completed its computation, a wave of NULL propagates behind it, returning the network to a neutral state and preparing it for the next computation.

The network breathes.

NULL → DATA → NULL → DATA

Each wavefront communicates not only values, but the state of the computation itself.

Synchronization is no longer imposed from outside the circuit.

It emerges naturally from the propagation of information.


This behavior is captured by what Fant called the completeness criterion.

A gate no longer reacts simply because one of its inputs changes.

Instead, it waits.

If all of its inputs contain valid data, the gate resolves its logical function and produces valid data at its output.

If all of its inputs have returned to NULL, the gate returns its output to NULL.

If neither condition has been satisfied, the gate does nothing.

Its current output is preserved through a small amount of hysteresis, or state-holding behavior, until the computation is genuinely complete.

The gate itself understands readiness.

It knows when to compute.

It knows when to wait.

No scheduler is required.

No polling loop is required.

No speculative execution is required.

The hardware already possesses the information needed to make the correct decision.


This is a profound departure from conventional Boolean logic.

Traditional logic gates are purely mathematical functions.

Given a set of inputs, they immediately produce an output.

NULL Convention Logic transforms them into participants in a larger computational conversation.

Every gate now understands not only what information it has received, but whether the information is complete.

Validity becomes part of the computation itself.


Viewed from this perspective, many familiar software abstractions begin to look surprisingly similar.

A mutex tells another thread that information is not yet available.

A queue tells a consumer that new information has arrived.

A future represents information that will exist later.

An Option<T> distinguishes between the existence and nonexistence of a value.

A Maybe<T> expresses the same idea.

Even the infamous null pointer exception exists because software attempted to use information that was never actually present.

These mechanisms are extraordinarily useful.

But they all compensate for the same missing capability.

The hardware itself cannot naturally express the difference between valid information and no information.

Software has spent decades rebuilding that distinction one abstraction at a time.


MatterScript fits naturally into this model.

Throughout this book we have described computation as the propagation of information.

We have replaced variables with tokens.

Instructions with events.

Connectivity with causality.

NULL Convention Logic contributes the final ingredient.

Validity.

A propagating token no longer carries only a value.

It also carries the knowledge that it exists.

Every computation knows when its inputs are complete.

Every downstream computation knows when it may begin.

Propagation becomes self-synchronizing.

The computational fabric no longer needs an external heartbeat to coordinate its behavior.

The information coordinates itself.


This idea also sheds new light on a concept we introduced earlier.

Information at rest is still part of the computation.

A gate holding valid data is not idle.

It is preserving the resolved state of a completed computation, ready to participate in whatever causal event comes next.

Likewise, a gate holding NULL is not empty.

It is communicating something equally important.

The previous computation has completed.

No valid information is presently available.

The network is ready for the next propagation.

Even absence has meaning.


This chapter began with a question that software has been asking for generations:

Is there valid information here yet?

NULL Convention Logic answers that question where it belongs.

In the computational fabric itself.

Once the hardware understands the difference between information and the absence of information, software no longer has to spend its life reconstructing that knowledge.

NULL is no longer merely the absence of a value.

It is information.

It tells the computational universe that one propagation has ended, another has not yet begun, and the fabric is prepared for whatever causal event arrives next.

In MatterScript, that idea completes a philosophy that has been building throughout this book.

Geometry carries information.

Placement carries information.

Causality carries information.

Tokens carry information.

And now we discover that even the absence of a token carries information.

Once every part of the computational fabric is allowed to express what it knows, computation begins to resemble something much closer to the physical universe itself—not a machine blindly executing instructions, but a connected world whose own structure tells it exactly when to act, and exactly when to wait.

Drafting in progress…

Completeness and Tokens

Earlier we introduced the idea that MatterScript does not manipulate variables.

It propagates tokens.

At first glance, a token may appear to be nothing more than a value moving through a computational network.

In reality, it is much more than that.

A token represents a complete assertion.

This idea lies at the heart of MatterScript’s computational model.


Most programming languages allow information to exist in partially completed states.

Consider a simple data structure.

struct Person {
    string firstName;
    string lastName;
    int age;
};

At any moment during execution, one field may have been initialized while the others remain empty.

The object exists.

But is the information complete?

Can another computation safely depend upon it?

Software spends enormous amounts of effort answering these questions.

Constructors establish initialization.

Flags indicate readiness.

Validation routines verify consistency.

Optional fields express uncertainty.

Throughout the software stack we repeatedly ask the same question:

Can this information be trusted yet?


MatterScript approaches the problem differently.

Information does not propagate simply because part of it has arrived.

It propagates only when it is complete.

Consider a simple invocation.

ADD($A $B)(SUM<>)

Suppose the value for $A arrives first.

Nothing happens.

The computation waits.

When the value for $B arrives, the situation changes.

Now every required input has been presented.

The invocation resolves.

A new token emerges.

That token is itself complete.

Completeness propagates through the network just as naturally as the values themselves.


This is precisely the idea embodied by the completeness criterion introduced in the previous chapter.

A computation does not begin because one input changed.

It begins because every required cause has been satisfied.

Likewise, a downstream computation does not need to ask whether its inputs are ready.

The arrival of a token already answers that question.

Validity is no longer reconstructed in software.

It is carried by the computation itself.


This changes what we mean by the word token.

A token is not simply a bit.

It is not merely a collection of bits.

It is not even just a value.

A token is a complete causal assertion.

It represents information whose originating computation has fully resolved.

When a token propagates, it carries two inseparable pieces of knowledge.

The value itself.

And the fact that the value is complete.


Engineers already rely on this idea in many other domains.

A network packet is not useful because it contains bytes.

It is useful because the receiver knows where the packet begins, where it ends, and whether it arrived intact.

A partially received packet is not treated as valid information.

It waits until the transmission is complete.

Only then does it become meaningful.

MatterScript extends this same principle beyond communication.

Every computation operates on complete information rather than fragments of an unfinished process.


The physical world behaves in much the same way.

A neuron does not fire because a single sodium ion crossed its membrane.

It fires when the membrane reaches a threshold.

A chemical reaction does not occur because one reactant has appeared.

It occurs when the necessary reactants are simultaneously present.

A seed does not become a tree because one condition has changed.

Growth begins when countless local conditions together make development possible.

Nature waits for completeness.

MatterScript does as well.


This has profound implications for causality.

Incomplete information cannot trigger downstream computation.

It cannot become the cause of another event because the conditions that produced it have not yet been satisfied.

Only complete tokens propagate.

Only complete tokens participate in the next causal relationship.

The compiler therefore gains an extraordinarily powerful guarantee.

Every propagation event represents a completed computation.

Every downstream computation begins with information that is already known to be valid.


Viewed from this perspective, conventional software and MatterScript describe very different worlds.

Traditional software propagates values.

MatterScript propagates completed facts.

Not simply,

“The temperature is 37 degrees.”

But,

“The temperature has been measured.”

Not merely,

“Collision equals true.”

But,

“A collision has occurred.”

Not simply,

“The account balance is 120 dollars.”

But,

“The account has been reconciled.”

The distinction may seem subtle.

It is not.

The first describes data.

The second describes completed events.

MatterScript is fundamentally a language of completed events propagating through a causal network.


This perspective also explains why MatterScript scales so naturally.

Every token represents the completion of one causal chain and the beginning of another.

Large computations therefore become compositions of complete assertions rather than collections of partially updated state.

There is no need for downstream computations to guess whether upstream work has finished.

The token itself provides that guarantee.

Synchronization is not layered on top of the computation.

It is inherent in the computation.


Information is valuable only when it can be trusted.

A token therefore represents more than a value.

It represents the completion of whatever process produced that value.

Every token is both data and evidence that the computation which created it has finished.

That simple observation changes the role of programming itself.

Programs are no longer systems that continually verify whether information is ready.

They become networks through which completed knowledge naturally propagates from one resolved computation to the next.

In MatterScript, information does not become complete because the programmer declares it to be complete.

It becomes complete because the computational fabric itself recognizes that every cause required to produce it has been satisfied.

Drafting in progress…

The Invocation Programming Language

Everything we have discussed so far—propagation, geometry, locality, completeness, causality, and tokens—needs a language capable of expressing those ideas.

MatterScript is that language.

Unlike most programming languages, MatterScript does not describe a sequence of instructions to execute. It describes a network of relationships through which information propagates.

Its syntax is intentionally small.

At its core, the language contains only four fundamental concepts:

  • Source and Destination Places
  • Invocations
  • Definitions
  • Name correspondence

Everything else emerges from these four ideas.


Places

The smallest unit of a MatterScript program is not a variable.

It is a place.

A place represents a location where information may exist.

Places come in two forms.

Destination places receive incoming information that will participate in a computation.

$SUM

Source places produce completed information after a computation has resolved.

SUM<>

Unlike variables, places do not own state.

They define the points through which information flows.

A destination place answers the question,

“Where is information flowing to?”

A source place answers,

“Where does resolved information flow from?”

This distinction may seem subtle at first, but it eliminates one of the oldest ambiguities in programming languages. A variable simultaneously represents storage, identity, value, and destination. MatterScript separates these ideas into explicit relationships.


Invocations

Places become useful only when they are connected.

An invocation establishes one of those connections.

ADD($A $B)(SUM<>)

Read this almost like a sentence.

Invoke the definition named ADD, using the information arriving at destination places $A and $B, and allow the completed result to emerge from the source place SUM<>.

Nothing executes here.

Nothing is assigned.

No function is “called” in the conventional sense.

The invocation simply declares a boundary through which information may propagate.

One side receives information.

The other side produces completed information.

The computation itself exists inside the associated definition.


Definitions

A definition describes how one set of relationships gives rise to another.

ADD[(A<> B<>)( $SUM )

    ...

]

Every invocation associates with a definition of the same name.

The correspondence is entirely symbolic.

An invocation named ADD resolves against the definition named ADD.

No addresses.

No pointers.

No jump tables.

Just names.

The interface between an invocation and its definition is beautifully symmetrical.

The destination places of the invocation associate, by position, with the source places of the definition.

The source places of the invocation associate, by position, with the destination places of the definition.

In other words, information enters through the invocation, flows into the definition, is resolved by the internal network, and then returns through the invocation back into the surrounding expression.

From there, the definition constructs an internal network of additional invocations, each connected through names.

Because every definition establishes its own naming domain, internal names never collide with names outside the definition.

A definition is therefore more than a reusable function.

It is an isolated and replicatable computational universe.


Name Correspondence

The fourth concept is the one that surprises almost everyone.

MatterScript has no explicit wiring syntax.

Connections arise through name correspondence.

OR($LEFT $RIGHT)(SUM<>)

NOT($SUM)(INVERTED<>)

AND($SUM $ENABLE)(RESULT<>)

The compiler recognizes that the source place SUM<> and every destination place named $SUM refer to the same point in the computational network. The connection is created automatically. If multiple destination places share the same correspondence name, the information naturally propagates to each of them without requiring explicit wiring.

Nothing else is required.

Name correspondence is therefore doing much more than replacing variables.

It expresses connectivity.

It expresses causality.

And, as we saw in earlier chapters, it expresses locality.

The names do not identify storage.

They identify relationships.


A Language of Relationships

Readers familiar with procedural languages often ask what MatterScript does not contain.

The answer is almost as revealing as what it does.

There are no primitive data types.

No predefined operators.

No built-in control structures.

No loops.

No if statements.

No memory addresses.

No variables.

No global state.

No sequence of execution.

No program counter.

No notion of “the next instruction.”

Those concepts are absent because the language is not describing execution.

It is describing a network.

Everything that appears dynamic in a conventional language emerges naturally from propagation through that network.


From One Dimension to Many

There is another remarkable property hidden beneath this deceptively simple syntax.

A MatterScript program is written as a one-dimensional stream of text.

Reality is not.

Electronic circuits occupy three-dimensional space.

Biological systems occupy three-dimensional space.

Chemical reactions unfold throughout physical space.

Weather develops across an entire atmosphere.

The purpose of the language is therefore not to imitate the geometry of the system directly.

Its purpose is to refer to that geometry.

Names identify relationships that cannot be expressed by adjacency in a line of text.

The compiler reconstructs those higher-dimensional relationships during compilation, assembling the computational fabric implied by the source code.

The program is not the computation.

It is a compact description of the computation.


A Different Kind of Programming Language

Most programming languages answer a familiar question.

“What should the processor do next?”

MatterScript answers a different one.

“How is information related?”

From that single question emerge propagation.

Locality.

Geometry.

Completeness.

Events.

Concurrency.

And even hardware itself.

The syntax is intentionally small because its purpose is not to describe every possible operation.

Its purpose is to describe the relationships from which every operation emerges.

Once you begin reading MatterScript this way, the language becomes surprisingly regular.

Every program is built from the same small vocabulary repeated over and over again.

Places.

Invocations.

Definitions.

And the names that bind them together.

Those four ideas are enough to describe computations ranging from Boolean logic to biological metabolism, from digital circuits to planetary simulations, because they describe not what a machine should do, but how information exists and propagates through the computational universe.

Numbers as Symbols

One aspect of MatterScript that often surprises programmers is its treatment of numbers.

Most programming languages inherit their notion of numbers directly from mathematics. An integer is understood to occupy a position on an infinite number line. Arithmetic operates by manipulating place-value representations—binary, decimal, hexadecimal, or some other radix.

MatterScript begins from a different premise.

A value is first and foremost a symbol.

Whether that symbol happens to represent the number 7, the amino acid lysine, the state of a traffic light, or the identifier of a chemical species is entirely determined by the relationships in which it participates.

There is nothing fundamentally special about numbers.

They are simply another kind of token.

Values Are Not Encodings

In conventional computing, the number 42 is represented as a sequence of bits.

Those bits are decoded, manipulated by arithmetic hardware, and then encoded again into another sequence of bits.

MatterScript does not require every value to be expressed as a place-value number.

Instead, a value may be represented directly as one member of a finite set of possible symbols.

If a computation distinguishes between fifty possible values, then there are simply fifty possible symbolic states.

Each state participates directly in the relationships that define the computation.

There is no requirement that every value first be embedded within a universal numeric encoding.

Direct Relationships

This seemingly small distinction has important consequences.

Traditional arithmetic gains tremendous flexibility by representing every number using a common structure.

The same adder can add one or one million because every value ultimately reduces to the same binary representation.

That generality comes at a cost.

Every computation must continually encode, decode, compare, and manipulate place-value numbers.

MatterScript allows a different approach.

When a computation naturally consists of a finite collection of states, those states can interact directly.

There is no number line.

There is no implicit ordering.

There is no requirement that neighboring symbols possess any mathematical relationship at all.

Only the relationships that matter to the computation need to exist.

Computation Without Arithmetic

Many real-world systems are not fundamentally arithmetic.

A protein may exist in one of several conformational states.

A traffic controller may distinguish among signal phases.

A biological cell may transition among metabolic conditions.

A packet on a network may occupy one of several protocol states.

These systems are typically forced into numeric representations because conventional computers fundamentally manipulate numbers.

MatterScript removes that assumption.

If the computation is symbolic, the representation can remain symbolic.

The compiler constructs the relationships directly, without first translating them into arithmetic.

Choosing the Right Representation

This does not mean that MatterScript abandons conventional numbers.

Arithmetic remains an important and efficient way to express many classes of computation.

Addition, multiplication, floating-point operations, and signal processing all continue to have natural representations.

The difference is that arithmetic is no longer the foundation upon which every other computation must be built.

It becomes one representation among many.

MatterScript allows the programmer to choose the representation that most naturally matches the problem being solved.

Sometimes that representation is a binary integer.

Sometimes it is a floating-point value.

And sometimes it is simply a collection of named symbols participating in a network of relationships.

In MatterScript, numbers are no longer privileged simply because conventional computers happen to perform arithmetic well.

They are treated as one kind of information among many, each represented in the form that most directly expresses the computation.

Drafting in progress…

Sequential Programs Are Special Cases

One of the oldest assumptions in computer science is so deeply embedded that most programmers never question it.

Programs execute one instruction after another.

If we wish to perform two things at once, we begin with sequential execution and then add mechanisms for concurrency.

Threads.

Processes.

Locks.

Queues.

Message passing.

Actors.

Coroutines.

Async runtimes.

From this perspective, concurrency appears to be an advanced feature layered on top of a fundamentally sequential world.

MatterScript begins from the opposite assumption.

Concurrency is the natural state of computation.

Sequential execution is simply one particular arrangement of causal relationships.


At first this sounds backwards.

Surely one thing at a time must be simpler than many things happening simultaneously.

After all, a sequential program has only one execution path.

A concurrent system may have thousands.

How could concurrency possibly be the simpler model?

The answer lies in a subtle confusion that has followed computer science since its mathematical beginnings.


When mathematicians describe an algorithm, they describe a sequence of symbolic transformations.

One step follows another.

The ordering itself is part of the definition.

That model is perfectly suited to proving theorems.

It is far less suited to describing physical systems.

The physical universe does not execute one event at a time.

Stars form while rivers flow.

Trees grow while clouds move.

Your heart beats while neurons fire.

Reality has never contained a program counter.

It contains countless independent processes interacting through local causal relationships.

Nature is concurrent by default.


The same is true inside every computer.

Billions of transistors switch simultaneously.

Signals propagate continuously through wires.

Memory arrays, caches, buses, and functional units all operate concurrently.

The familiar illusion of sequential execution exists because the hardware carefully coordinates these activities so they appear to produce one ordered stream of instructions.

Sequential software is therefore not the absence of concurrency.

It is a particular organization of concurrency.


This distinction becomes clearer when we consider a simple example.

Imagine two independent computations.

Measure temperature.

Update the display.

Nothing about these activities requires one to happen before the other.

They may proceed simultaneously.

Now imagine introducing a new requirement.

Measure temperature.

Only after the measurement completes,

update the display.

We have not changed the computations themselves.

We have merely introduced a causal relationship.

One event must now wait for another.

Sequentiality has emerged naturally from an additional constraint.


This observation generalizes remarkably well.

Every sequential program can be viewed as a network in which each computation depends upon the completion of the previous one.

A → B → C → D → E

Nothing mysterious has happened.

We have simply constructed the narrowest possible causal graph.

Every node has exactly one predecessor and one successor.

It is still a propagation network.

It simply contains no independent branches.

Sequential execution is therefore a special case of concurrent computation whose dependency graph happens to collapse into a single chain.


Seen from this perspective, many of the traditional difficulties of concurrency begin to look different.

Programmers often speak of race conditions, deadlocks, livelocks, and nondeterminism as though they are unavoidable consequences of allowing multiple computations to occur simultaneously.

In reality, these problems arise because we have described insufficient causal relationships.

If two computations may produce different outcomes depending upon which one happens first, then the computation has failed to specify what must happen before what.

The ambiguity is not in the hardware.

It is in the description.


MatterScript eliminates much of this uncertainty by making causality explicit.

Names establish relationships.

Tokens propagate only when computations are complete.

Events occur because their causes have been satisfied.

The compiler constructs a network whose dependencies are already known before execution begins.

There is no need to recover ordering through locks, polling, or carefully timed delays.

The ordering already exists wherever it is logically necessary.

Everywhere else, the computation proceeds independently.


This dramatically changes the way we think about parallelism.

In conventional software, programmers often begin with a sequential algorithm and then search for opportunities to parallelize it.

MatterScript reverses the process.

The programmer describes the causal structure of the problem.

The compiler discovers which portions are inherently independent.

Parallelism is no longer something added to a sequential program.

It is simply the natural consequence of relationships that do not depend upon one another.


This is exactly how the physical world behaves.

A chemical reaction does not pause while another reaction occurs elsewhere in the solution.

A neuron does not wait for every neuron in the brain before producing a signal.

A weather system does not compute one cloud at a time.

Independent events proceed independently.

Only genuine causal relationships impose ordering.

MatterScript expresses computation in precisely the same way.


Perhaps the greatest misconception surrounding concurrency is the belief that it is inherently nondeterministic.

What is actually nondeterministic is an incompletely specified system.

When the necessary causal relationships are absent, multiple orderings become possible, and different orderings may produce different results.

The solution is not to force everything into a single sequential stream.

The solution is to describe the missing relationships.

Once those relationships exist, the computation becomes deterministic regardless of the relative speeds of its individual parts.

The hardware is free to propagate information as quickly or as slowly as physics allows.

The outcome remains the same because causality—not timing—determines the result.


This is one of the central ideas behind MatterScript.

Programs are not fundamentally sequences of instructions.

They are networks of causes and effects.

Some portions of those networks naturally execute one after another.

Others naturally proceed simultaneously.

Neither behavior is privileged.

Both emerge from exactly the same language.

Sequentiality is no longer the primitive from which concurrency must be constructed.

It is simply one possible shape that a causal network may take.


For decades we have taught programmers to begin with sequence and cautiously introduce parallelism where it appears safe.

MatterScript invites us to reverse that perspective.

Begin with the relationships.

Allow every independent computation to exist independently.

Introduce ordering only where the problem itself requires it.

Seen this way, sequential programs are not the foundation of computation.

They are its simplest special case.

Drafting in progress…

Part IV: Learning MatterScript

This is the language tutorial.

Rather than teaching isolated syntax rules, each chapter builds progressively larger systems while explaining the design decisions behind every feature.

By the end of this section readers can comfortably write MatterScript programs from scratch.

Drafting in progress…

Language Overview

Understanding a MatterScript Definition

If you’ve written software before, the first thing you’ll notice about MatterScript is what isn’t there.

There are no variables.

There are no assignments.

There are no if statements.

There are no loops.

Instead, a MatterScript program describes a network of computations connected together by named places. Values don’t move through a sequence of instructions—they propagate through a graph. Every definition is a small network, and larger systems are built by composing those networks together.

Let’s look at a complete example.

// linear: TAG-129 Imperative form of expression

FULLADD(0, 1, 0)(< > CARRYOUT < >)  $CARRYOUT

FULLADD[(X< >Y< >C< >) ($SUM $CARRY)
NOT($X)(OP1< >)
AND($OP1 $Y)(OP4< >)
NOT($Y)(OP2< >)
AND($X $OP2)(OP3< >)
OR($OP4 $OP3)(FIRSTSUM< >)
NOT($FIRSTSUM)(OP6< >)
AND($C $OP6)(OP7< >)
NOT($C)(OP5< >)
AND($OP5 $FIRSTSUM)(OP8< >)
OR($OP7 $OP8)(SUM< >)
AND($X $Y)(OP10< >)
AND($C $FIRSTSUM)(OP9< >)
OR($OP10 $OP9)(CARRY< >)
: OR[(A< > B< >)($res) res<$A$B()> :0,0[0] 0,1[1] 1,0[1] 1,1[1] ]
  AND[(A< > B< >)($res) res<$A$B()> :0,0[0] 0,1[0] 1,0[0] 1,1[1] ]
  NOT[(A< >)($res) res<$A()> :1[0] 0[1] ] ]

At first glance this looks unusual, but there are really only three ideas at work.

Definitions

Every MatterScript program is built from definitions. If you’re coming from a conventional programming language, the closest analogy is a function or module, but a MatterScript definition describes a network of relationships rather than a sequence of instructions.

A definition begins with a name followed by two interface lists.

FULLADD[(X<> Y<> C<>) ($SUM $CARRY)

The first list declares the source places that provide information to the definition.

The second list declares the destination places that the definition will eventually produce.

You can think of these as the external interface of the network. Everything inside the definition exists to transform information arriving at the source places into information that eventually reaches the destination places.

Unlike an imperative function, a MatterScript definition doesn’t “run” from top to bottom. It becomes active as values propagate through its internal network. Once every dependency needed by a particular invocation is complete, that portion of the network resolves naturally.


Invocations

The body of a definition consists almost entirely of function invocations.

NOT($X)(OP1<>)

AND($OP1 $Y)(OP4<>)

OR($OP4 $OP3)(FIRSTSUM<>)

Each line says exactly one thing:

Invoke this definition using these named inputs and place the result into this named output.

Nothing is assigned.

Nothing is overwritten.

Each invocation simply adds another node to the growing computation graph.

Notice that the order of the statements isn’t particularly important. Unlike an imperative language, MatterScript doesn’t execute one statement after another. An invocation becomes active as soon as every destination place it references contains a complete value. The apparent sequence of the source code exists primarily to make the network readable for humans.


Names Create Connections

Perhaps the most unusual feature of MatterScript is that wires are never drawn explicitly.

Instead, names create the connections automatically.

When the output of one invocation is named FIRSTSUM…

OR($OP4 $OP3)(FIRSTSUM<>)

…another invocation can consume it simply by referring to $FIRSTSUM.

AND($C $FIRSTSUM)(OP9<>)

No explicit wiring statement is needed. The compiler recognizes that both names refer to the same place and connects them together automatically.

Throughout a MatterScript program, names define connectivity, not variable storage.

But connectivity is only part of the story.

By describing which computations produce information and which computations consume it, a MatterScript program also defines causality. Every connection identifies not only where information flows, but what can cause something else to happen. The compiler therefore constructs more than a network of wires—it constructs a network of cause-and-effect relationships.

This distinction becomes increasingly important as systems grow larger.

In conventional software, a simulation often advances by repeatedly scanning every element in the model to determine whether anything has changed. Whether simulating molecules, ecosystems, electrical grids, or weather systems, the processor continually revisits enormous amounts of state simply to discover where the next computation should occur.

MatterScript approaches the problem differently.

Because the causal relationships are known at compile time, the compiler already knows which computations are affected by each result. When information propagates, only those computations whose inputs have changed become active. The computation follows the structure of the problem itself rather than repeatedly searching for work to do.

This idea extends far beyond digital logic. In later chapters, we’ll use the same language constructs to describe chemical reactions, physical simulations, biological systems, and large-scale computational models. Although these systems appear very different, they share the same underlying principle: local causes produce local effects, and global behavior emerges from the propagation of those effects through a connected network.

Rather than describing how values are copied from one location to another, MatterScript describes the relationships that govern both connectivity and causality, allowing the compiler to construct a computational fabric that mirrors the structure of the problem itself.


Constant Definitions

Most definitions contain an internal network of invocations. Some definitions, however, are much simpler.

A constant definition contains no input interface, no internal definitions, and no resolution expression. It consists only of a name and a value.

ONE[1]

ZERO[0]

Because there are no inputs to resolve, the value is immediately returned whenever the definition is invoked.

Although these look trivial, constant definitions are one of the fundamental building blocks of MatterScript. They define the value transformation rules that describe how information moves through a network.

Consider the AND gate introduced earlier.

AND[(A<> B<>)($res)

res<$A$B()>

:0,0[0]
 0,1[0]
 1,0[0]
 1,1[1]
]

The lines following the colon are themselves constant definitions.

Each key on the left represents a possible invocation name formed by concatenating the values arriving on $A and $B.

When $A contains 1 and $B contains 0, the composed name becomes 10. The compiler locates the constant definition named 10 and returns its associated value—in this case 0.

While the formed symbols are the concatenaed values like 00 and 01, for correct compiler interpretation of separate component symbols, you as the developer register your intent in the source code by separating the content values with the general separator comma ‘,’.

Rather than executing conditional logic, MatterScript performs a lookup. The incoming token values determine which constant definition is selected, and that definition immediately returns its contents to the invocation.

Taken together, these constant definitions form the complete truth table of the AND gate.

This idea extends far beyond Boolean logic. A constant definition doesn’t have to return a single integer. It can return an entire fragment of MatterScript that is inserted back into the network and resolved as if it had been written directly in place.

For example, a selector might choose between two different computation paths:

A[...]

B[...]

SELECT[(select<>)($out)

$out<$select()>

:A[A()]
 B[B()]
]

If select contains A, the constant definition A[...] is selected and its contents are returned to the place of invocation. If select contains B, the alternate definition is returned instead.

In this sense, constant definitions are much more than literals. They are the language’s mechanism for expressing lookup tables, truth tables, compile-time dispatch, and even dynamic substitution of computation. Throughout MatterScript you’ll encounter constant definitions everywhere—from primitive logic gates to large physics lookup tables generated automatically by the compiler.


Primitive Definitions

At the end of the full-adder listing are three more definitions.

AND[...]

OR[...]

NOT[...]

These are not built-in language keywords. They are ordinary MatterScript definitions written in exactly the same language as every other component.

The only difference is that they terminate in collections of constant definitions rather than invoking additional networks.

:0,0[0]
 0,1[0]
 1,0[0]
 1,1[1]

This collection of constant definitions completely specifies the behavior of the AND gate.

Likewise, the OR and NOT definitions each contain their own independent lookup tables. Even though all three definitions use names such as A, B, and res, those names exist only within the definition in which they are declared. There is no possibility of collision because every definition has its own private scope.

This is an important design principle: there are no privileged language primitives. Even the simplest logical operators are expressed using the same mechanisms available to every MatterScript programmer.


Reading MatterScript

A useful way to read MatterScript is to ignore the formatting at first and focus on the flow of information.

Every definition follows the same pattern:

  1. Declare the external interface.
  2. Invoke other definitions.
  3. Connect them together through shared names.
  4. Produce one or more outputs.

Once you begin thinking in terms of connected computations rather than sequential instructions, the language becomes surprisingly regular. Large systems are built from the same small vocabulary repeated over and over: definitions, places, invocations, and constant definitions.

The full adder shown here may appear very different from an equivalent implementation in C or Verilog, but conceptually it is doing something remarkably simple. It is describing a network of relationships. The compiler’s job is to transform that description into executable hardware while preserving those relationships exactly.

Files and Projects

Drafting in progress…

Definitions

If invocations are the doorways through which information enters and leaves a computation, definitions are the computations themselves.

Every definition describes a self-contained network of relationships. It receives information through its input boundary, transforms that information by connecting together other definitions, and presents the completed result through its output boundary.

A definition always begins with a name.

FULLADD[(X<> Y<> C<>)( $SUM $CARRY )

    ...

]

That name serves two purposes.

First, it gives the definition an identity that other parts of the program can invoke.

Second, it creates a completely independent naming domain. Every correspondence name declared inside the definition exists only within that definition. Internal names can be chosen for clarity without worrying about collisions elsewhere in the program.

Like a function in other languages, a definition may be reused many times. Unlike a conventional function, however, a definition is not a sequence of instructions. It is a description of a network.


The Interface

Every definition begins with two lists enclosed in parentheses.

(X<> Y<> C<>)( $SUM $CARRY )

The first list declares the source places through which information enters the definition.

The second list declares the destination places through which completed results leave the definition.

These two lists form the public interface of the definition.

Everything inside the definition exists solely to transform the incoming relationships into the outgoing ones.

One subtle aspect of MatterScript is that these interface lists are mirror images of an invocation.

When an invocation supplies information through its destination places, those values appear at the corresponding source places of the definition.

Likewise, when the definition produces results through its destination places, those results emerge from the corresponding source places of the invocation.

This inversion often seems unusual at first, but it reflects the flow of information rather than the layout of the source code. Information enters one boundary, propagates through the internal network, and exits through the opposite boundary.

Once you begin thinking in terms of propagation instead of parameter passing, the symmetry becomes surprisingly natural.


The Body of a Definition

After the interface comes the body of the definition.

This is where the computation is described.

NOT($X)(OP1<>)

AND($OP1 $Y)(OP4<>)

OR($OP4 $OP3)(FIRSTSUM<>)

...

Notice what is missing.

There are no assignments.

No variables.

No control flow.

No sequence of execution.

Each invocation simply introduces another relationship into the network.

Together, these invocations form a graph of causes and effects that the compiler later transforms into an executable implementation.

The source code is written linearly because humans read text one line at a time.

The computation itself is not linear.


Definitions Build Definitions

One of the most elegant aspects of MatterScript is that every definition is built exactly the same way.

A full adder is constructed by invoking AND, OR, and NOT.

An arithmetic logic unit may be constructed by invoking full adders.

A processor may be constructed by invoking arithmetic logic units.

A computer may be constructed by invoking processors, memories, and communication fabrics.

At every level, the language remains unchanged.

There is no distinction between primitive expressions and large systems.

The same syntax that describes a Boolean gate can also describe a distributed simulation, a robotic control system, or a biological pathway.

Only the scale changes.


Isolation Through Correspondence

Because every definition establishes its own correspondence domain, names are always interpreted locally.

The name SUM inside a full adder has no relationship to a SUM inside another definition unless one is explicitly connected through an invocation.

This isolation allows definitions to remain completely self-contained.

You never need to invent globally unique names.

You simply describe the relationships within the current definition, and the invocation boundary handles the correspondence with the outside world.

In many languages, managing scope is largely about preventing accidental collisions.

In MatterScript, the syntax naturally prevents them.


A Definition Is a Small Universe

It is tempting to think of a definition as a function because the syntax appears familiar.

A better analogy is a physical system.

The interface defines the boundary of that system.

Inside the boundary is a network of interacting components.

Information enters, propagates according to local relationships, and eventually emerges in a transformed form.

Nothing outside the boundary needs to know how the internal network is organized.

Nothing inside the boundary needs to know where its inputs originated or where its outputs will ultimately go.

The definition is a complete computational universe in miniature.

It describes not a sequence of actions, but a set of relationships whose behavior emerges naturally from the propagation of information.

Drafting in progress…

Places

Every programming language has a fundamental unit of expression.

In C, it is the variable.

In Lisp, it is the list.

In SQL, it is the relation.

In MatterScript, it is the place.

A place is where information exists.

Not memory.

Not storage.

Not a variable.

A place.

This may seem like a small difference in terminology, but it reflects a fundamentally different way of thinking about computation.

Variables are containers whose contents change over time.

Places are locations through which information flows.


Information Exists Somewhere

Earlier in this book we introduced one of MatterScript’s guiding principles:

Information always exists somewhere.

Places are how the language expresses that idea.

Every token occupies a place.

Every computation consumes information from places and produces information into other places.

Every causal relationship in a MatterScript program is ultimately expressed as a relationship between places.

If you understand places, you understand the foundation of the language.


Two Kinds of Places

MatterScript distinguishes between two kinds of places.

A source place is where information emerges after a computation has completed.

SUM<>

A destination place is where information arrives to participate in another computation.

$SUM

Although the syntax differs by only two characters, the distinction is important.

A source place represents the origin of a completed result.

A destination place represents the destination of information that will be resolved.

Every propagation in MatterScript begins at a source place and ends at one or more destination places.


Names Create Association

Unlike a schematic, MatterScript never draws wires.

Instead, places associate through correspondence names.

OR($A $B)(SUM<>)

NOT($SUM)(INVERTED<>)

AND($SUM $ENABLE)(RESULT<>)

Here, the source place SUM<> associates automatically with every destination place named $SUM.

The compiler constructs the connections.

Nothing else is required.

One source place may associate with many destination places.

This is the language’s natural form of fan-out.

Information propagates simultaneously to every associated destination without requiring explicit duplication or copying.


Places Are Not Variables

It is tempting to think of a place as another name for a variable.

It is not.

A variable represents mutable storage.

A place represents a point of association.

The distinction matters.

Variables are updated.

Places are connected.

Variables emphasize ownership.

Places emphasize relationships.

Variables encourage us to ask,

“What value does this object contain?”

Places encourage us to ask,

“What computations are related?”

This shift in perspective is one of the most important conceptual transitions in learning MatterScript.


One Source, Many Destinations

Every correspondence name identifies exactly one source place.

There cannot be two source places with the same name within the same correspondence domain.

If there were, the compiler could not determine which source should provide the information.

Destination places, however, may appear as many times as needed.

FIRSTSUM<>

...

AND($FIRSTSUM $C)(...)

...

NOT($FIRSTSUM)(...)

...

OR($FIRSTSUM $ENABLE)(...)

Each destination named $FIRSTSUM receives the same propagated token.

No explicit fan-out operator is necessary.

The correspondence itself defines the connectivity.


Places Define the Network

As a MatterScript program grows, it becomes tempting to think that invocations are the primary objects.

In reality, the opposite is true.

Invocations exist because they connect places.

Definitions exist because they organize places.

Propagation occurs because places are associated.

Completeness is evaluated at places.

Tokens reside in places.

Geometry, as we will see later, gives places physical location.

Everything else in the language is built around them.


From Names to Geometry

In this chapter, a place is identified by its correspondence name.

That is enough to express logical connectivity.

Later in the book, we will extend this idea.

A place may have not only a name, but also a position in physical space.

When that happens, the language begins to describe not merely what is connected, but where those relationships exist.

Names establish logical structure.

Geometry will establish physical structure.

Together they allow MatterScript to describe computational systems with the same fidelity that semiconductor manufacturing describes physical ones.


A Computational Location

Perhaps the simplest way to think about a place is this:

A place is not a piece of memory.

It is not a variable.

It is not an address.

A place is a location in the computational universe.

Information appears there.

Relationships connect there.

Causes arrive there.

Effects depart from there.

Everything else in MatterScript exists to describe how information moves from one place to another.

Drafting in progress…

Tokens

In recent years, the word token has become part of every programmer’s vocabulary.

Large language models consume streams of tokens representing words, punctuation, and fragments of language. Those tokens move through billions of parameters, gradually transforming into new tokens that become the model’s response.

MatterScript uses the same word, but in a much broader sense.

A token is simply a unit of information.

It may represent a number.

A symbol.

A Boolean value.

A chemical species.

A network packet.

A state in a finite-state machine.

Or even another fragment of computation.

Language is only one possible kind of token.


Information in Motion

Earlier we introduced the idea that computation is propagation.

Tokens are the things that propagate.

A place does not “contain a variable.”

It temporarily presents a token.

That token moves through the network according to the causal relationships expressed by the program.

SUM<1>

...

AND($SUM $ENABLE)(RESULT<>)

Here the token 1 emerges from the source place SUM<>, propagates to every destination place named $SUM, and participates in the next computation.

Nothing has been assigned.

Nothing has been copied.

The information has simply propagated through the network.


Tokens Have Meaning Because of Relationships

One of the most important differences between MatterScript and conventional programming is that a token has no intrinsic meaning.

The symbol

ACTIVE

is not inherently more significant than

7

or

GLYCINE

Its meaning comes entirely from the relationships in which it participates.

If a definition recognizes ACTIVE, then it becomes part of that computation.

If another definition recognizes GLYCINE, then it becomes part of an entirely different one.

MatterScript does not privilege numbers over symbols.

Every token is simply information participating in a network of relationships.


Tokens Are Not Objects

Object-oriented programmers may be tempted to think of tokens as tiny objects moving through the program.

That is not quite right.

Objects combine identity, storage, behavior, and often lifetime into a single construct.

Tokens do none of those things.

A token is simply information.

The network provides the behavior.

Places provide the locations.

Definitions provide the transformations.

The token merely carries information from one relationship to the next.


Tokens Carry Potential

One of the recurring themes of this book is that computation exists not only in motion, but also in possibility.

A token resting at a place is not inactive.

It represents a computation waiting for its remaining causes.

Once every required token has arrived, the definition resolves, produces new tokens, and propagation continues.

This idea should feel familiar.

A transistor waiting for its inputs is still part of a computation.

A molecule waiting for another reactant is still part of a chemical reaction.

A neuron waiting for enough excitation is still participating in thought.

The token represents the information currently present.

The relationships determine what may happen next.


Tokens Beyond Software

Because tokens are simply information, MatterScript naturally extends beyond conventional software.

In a biological simulation, a token might represent the concentration of a signaling molecule.

In a traffic simulation, it might represent the state of an intersection.

In a manufacturing model, it could represent the presence of a component on an assembly line.

In an AI system, it may literally represent a language token.

The language itself does not distinguish among these cases.

Each is simply information flowing through a network of places.


The Atoms of Computation

If places define the geometry of the computational universe, then tokens are its atoms.

They occupy places.

They propagate through relationships.

They trigger events.

They participate in transformations.

Everything that changes within a MatterScript program ultimately changes because tokens move.

The language does not begin with variables or instructions.

It begins with information itself.

Variables store information. Tokens are information.

In imperative languages, information is trapped inside mutable containers. In MatterScript, information is the thing that moves, and the computation emerges from its movement through a network of relationships.

Drafting in progress…

Field of Resolution

At the heart of every definition is a field of resolution.

This is where computation happens.

Not by executing instructions one after another, but by allowing tokens to propagate through a bounded network of relationships until every possible consequence has been expressed.

A field of resolution is the computational interior of a definition.

The interface describes what enters and what leaves.

The field of resolution describes what happens in between.

FULLADD[(X<> Y<> C<>)( $SUM $CARRY )

    U1: NOT($X)(OP1<>)
    AND($OP1 $Y)(OP4<>)
    OR($OP4 $OP3)(FIRSTSUM<>)

    ...

]

Everything between the interface and the contained definitions belongs to the field of resolution.

Understanding Invocation Labels

Function: Explicit instance labels (such as u1: attached to NOT($X)(OP1<>)) assign a unique identifier to a specific component instance in the netlist. This is particularly useful during hardware code generation (e.g., VHDL) to maintain stable component instantiation paths, prevent multiple-driver errors, and make netlist debuggers much easier to read.

Optionality: Invocation labels are completely optional. As shown above, subsequent invocations like AND and OR omit the prefix label and rely on standard anonymous or compiler-generated handling.


A Local Universe

One useful way to think about a field of resolution is as a small universe with its own internal laws.

Tokens enter through the definition’s source places.

They propagate through local relationships.

New tokens are produced.

Eventually the completed results appear at the definition’s destination places.

Nothing outside the definition needs to know how that process unfolds.

The internal network is completely encapsulated.

This is not merely an organizational convenience.

It is what allows MatterScript to compose arbitrarily large systems from small, understandable pieces.


Resolution Is Local

One of the recurring themes of this book is locality.

Every interaction occurs because information has arrived at a particular place.

Fields of resolution apply the same principle to entire computations.

A definition never scans the rest of the program looking for work.

It never asks whether another computation has finished.

It never waits for a scheduler to grant permission to execute.

It simply responds to the arrival of complete information at its own boundary.

Everything it needs to know already exists within its local neighborhood.

This is one of the reasons MatterScript scales naturally from tiny logic gates to systems containing billions of interacting elements.


Boundaries Matter

The boundary of a field of resolution is more than syntax.

It defines where causality begins and ends.

Outside the boundary, the definition is simply a named component.

Inside the boundary, the compiler can freely analyze propagation, construct dependency graphs, optimize placement, and ultimately generate hardware or software that preserves the expressed relationships.

Because the internal implementation is isolated, a definition can be rewritten, optimized, or even mapped onto an entirely different execution substrate without changing its external behavior.

Only the relationships at the boundary matter.


A Reaction Chamber

The word resolution was chosen deliberately.

The goal is not to execute code.

The goal is to resolve relationships.

A useful analogy is a chemical reaction vessel.

Reactants enter.

Local interactions occur according to the laws governing that system.

Products leave.

The vessel itself does not orchestrate the reaction.

It simply provides the environment in which the reaction can occur.

A resolution area serves the same purpose.

It defines the space within which information is allowed to interact.


Resolution Before Execution

Most programming languages ask a familiar question:

What instruction executes next?

MatterScript asks a different one:

What relationships can now be resolved?

That difference may seem subtle, but it changes the entire computational model.

The compiler is no longer concerned with constructing an execution order.

Instead, it constructs a network in which every local relationship resolves as soon as its required information is complete.

Execution becomes an implementation detail.

Resolution is the language itself.


Computation Happens Inside Relationships

As we’ve progressed through this chapter, a pattern has begun to emerge.

Places define where information exists.

Tokens define the information itself.

Invocations connect one computation to another.

Definitions establish local boundaries.

Fields of resolution are where those relationships come alive.

The language is gradually revealing itself not as a sequence of operations, but as a landscape of interacting regions, each resolving its own local relationships while contributing to the behavior of the larger system.

Drafting in progress…

Transform Rules

Transform Rules

As we have seen throughout this book, MatterScript builds complex behavior by composing definitions into increasingly larger networks. A full adder is built from logic gates. A processor is built from arithmetic units. A simulation is built from thousands or millions of interacting definitions.

Eventually, however, every hierarchy reaches a point where there is nothing left to decompose.

That is where transform rules come in.

Transform rules describe behavior directly. Rather than expressing a computation by invoking other definitions, they specify the relationship between an input (or set of inputs) and its corresponding output.

AND[(A<> B<>)($res)

    res<$A$B()>

    :0,0[0]
     0,1[0]
     1,0[0]
     1,1[1]
]

Here, the place of resolution forms the correspondence name 00, 01, 10, or 11 from the arriving input tokens. That name selects one of the contained constant definitions, whose value is immediately returned as the result.

Nothing is executed.

Nothing is searched.

The behavior is simply declared.


Computation as Transformation

In most programming languages, we think of computation as a sequence of operations.

An input is loaded into memory.

Instructions execute.

Intermediate values are produced.

Eventually a result is returned.

MatterScript approaches the problem from a different direction.

Every definition answers a single question:

Given these input tokens, what should this relationship produce?

Sometimes that answer requires an entire network of interacting definitions.

Sometimes the answer is already known.

Transform rules express those known relationships directly.

Rather than describing how to compute a result, they declare what the result is.


The Leaves of the Network

A useful way to think about transform rules is as the leaves of the computational tree.

Higher-level definitions are composed from smaller definitions.

Those definitions are composed from still smaller ones.

Eventually the hierarchy terminates in a set of transform rules that require no further decomposition.

The language itself makes no distinction between these levels.

A full adder and an AND gate are both definitions.

One happens to be implemented by composing other definitions.

The other happens to terminate in transform rules.

Composition, not special language primitives, is what gives the language its expressive power.


More Than Lookup Tables

It is tempting to think of transform rules as lookup tables.

While the compiler may ultimately implement them that way, the language is expressing something more fundamental.

Each rule defines a relationship between information entering the definition and information leaving it.

:0,0[0]
 0,1[0]
 1,0[0]
 1,1[1]

These are not rows in a database.

They are declarations of causality.

When the incoming tokens form the name 11, the relationship produces the token 1.

When they form 00, the relationship produces 0.

The emphasis is not on storage, but on transformation.


Building Without Built-ins

One of MatterScript’s design principles is that there are no privileged language primitives.

AND, OR, and NOT are not keywords.

They are ordinary definitions.

The only difference is that they happen to terminate in transform rules rather than invoking additional definitions.

This uniformity makes the language remarkably scalable.

Whether you are describing a two-input logic gate, a cryptographic accelerator, a biological pathway, or a distributed simulation, every component is expressed using the same vocabulary of places, definitions, invocations, and transformations.

The language never changes.

Only the size of the network does.


A Foundation for Higher-Level Systems

As MatterScript evolves, most transform rules will never be written by hand.

Compilers will synthesize them from Boolean minimization.

Geometry engines will generate them from spatial relationships.

Domain-specific libraries may derive them from physical laws, chemical reactions, or mathematical models.

To the language, however, they are all the same.

Every transform rule is simply a definition whose behavior is expressed directly rather than composed from smaller parts.

That uniformity is one of MatterScript’s greatest strengths.

At every level of abstraction, from a single logic gate to a planetary simulation, computation is described using the same fundamental idea:

Information arrives. Relationships resolve. New information emerges.

Drafting in progress…

Invocation

Every definition describes a local piece of computation.

An invocation is how those pieces are connected together.

If definitions are the building blocks of a MatterScript program, invocations are the joints that hold those blocks together.

They do not execute a function.

They establish a relationship.


Invoking a Definition

An invocation consists of three parts:

AND($A $B)(RESULT<>)

The definition name:

AND

The destination list:

($A $B)

And the source list:

(RESULT<>)

Read aloud, this simply means:

Resolve the definition named AND using the information arriving at $A and $B, then propagate the resulting token from RESULT<>.

Notice what is missing.

There is no assignment.

There is no return statement.

There is no instruction pointer moving into another function.

The invocation merely connects one region of the computational network to another.


Interfaces, Not Calls

Programmers often think of function calls as transferring control.

MatterScript does not transfer control.

It transfers relationships.

When an invocation associates with a definition, the invocation’s destination places become the definition’s source places.

The definition’s destination places become the invocation’s source places.

The definition behaves as though it were physically inserted into the surrounding network.

Nothing is copied.

Nothing is executed “inside” another function.

The interface simply joins two parts of the larger computational fabric.


Daisy-Chaining Computation

Karl Fant describes this relationship as daisy-chaining.

One boundary connects directly to the next.

Conceptually, the invocation and definition fit together like matching connectors.

Invocation

($A $B) ---> Definition Inputs

Definition Outputs ---> (RESULT<>)

This interface is entirely syntactic.

Names inside the definition remain private.

Only the boundary is visible from the outside.

That makes definitions naturally composable.

A definition can be rewritten internally without affecting anything connected to it, provided its interface remains the same.


Invocations Build Networks

An invocation does not perform work by itself.

Its purpose is to describe how work is connected.

Consider the beginning of the full adder.

NOT($X)(OP1<>)

AND($OP1 $Y)(OP4<>)

OR($OP4 $OP3)(FIRSTSUM<>)

Each invocation contributes another node to the growing network.

Notice how every result immediately becomes available to subsequent relationships through name correspondence.

The program grows outward like a circuit diagram rather than downward like a call stack.

By the time the compiler has processed the definition, these individual invocations have become a connected graph of causal relationships.


Invocation Is Composition

This is perhaps the most important idea in the language.

An invocation is not asking another definition to perform work.

It is composing a larger computation from smaller ones.

That distinction may seem subtle at first, but it changes how we think about programs.

Instead of imagining a processor jumping from function to function, imagine an architect assembling a bridge from prefabricated components.

Each component contributes its own local behavior.

Together they form a larger structure.

MatterScript programs are assembled in exactly this way.


Local Boundaries, Global Behavior

One remarkable consequence of invocations is that every definition remains completely local.

Each definition knows only its own interface.

It has no knowledge of the larger system in which it participates.

Yet when thousands—or millions—of definitions are connected together through invocations, coherent global behavior emerges.

This is the same principle we have encountered throughout the book.

Local relationships produce global computation.

No central controller is required.


One Language at Every Scale

Because invocations simply connect definitions, there is no distinction between small and large systems.

A primitive logic gate is invoked exactly the same way as a processor.

A processor is invoked exactly the same way as a network protocol.

A biological model could be invoked exactly the same way as a weather simulation.

The syntax never changes.

Only the definitions become larger.

This consistency is one of MatterScript’s greatest strengths.

The language scales by composition rather than by accumulating new abstractions.


Building Computational Fabric

If definitions are the vocabulary of MatterScript, invocations are its grammar.

They tell the compiler how individual pieces relate to one another.

Not in time.

Not through memory.

But through causality.

Each invocation extends the computational fabric, joining one local network to another until the complete system emerges as a single connected web of relationships.

Nothing is “called.”

Everything is connected.

Drafting in progress…

Name Composition

Names are one of MatterScript’s most powerful concepts.

Throughout this book we’ve seen names connecting places, selecting transform rules, and establishing causality between independent parts of a computation.

But names are not limited to simple identifiers.

They can also be constructed dynamically from the information flowing through the network.

This process is called name composition.


Building a Name

Consider the definition of an OR gate.

OR[(A<> B<>)($result)

    $A$B()

    :0,0[0]
     0,1[1]
     1,0[1]
     1,1[1]
]

As tokens arrive at A<> and B<>, they are combined to form a single correspondence name.

If the arriving tokens are 0 and 1, the composed name becomes:

01

That composed name is then used to select the matching transform rule.

The computation is not performing a comparison.

It is forming a name and allowing the language’s correspondence mechanism to resolve it.


Names Are Structures

It is tempting to think of a composed name as a string.

That would miss the larger idea.

MatterScript treats names as structured relationships.

Each component contributes part of the completed correspondence.

The resulting name represents the combined state of all participating inputs.

This is why transform rules feel so natural.

Rather than asking,

“If A equals 0 and B equals 1…”

the language simply forms the name represented by those arriving tokens.

That name either corresponds to a rule or it does not.


Helping the Parser

You may have noticed that the transform rules include commas.

:0,0[0]
 0,1[1]
 1,0[1]
 1,1[1]

Internally, the correspondence name is simply the combination of the arriving tokens.

The commas are not part of the name itself.

Instead, they exist for the benefit of the programmer and the parser.

Without separators, a rule such as

011

could represent several different structures depending on how the incoming tokens are partitioned.

Did it come from:

0 11

or

01 1

or three independent tokens?

The commas remove that ambiguity.

They tell the parser exactly which portion of the composed name corresponds to each incoming place.

They do not change the meaning of the composed name.

They simply make the programmer’s intent explicit.


Implicit Values vs. Explicit Invocations

Because MatterScript bridges pure value relations, associative behavior, and traditional functional paradigms, you will sometimes see two different forms for triggering correspondence resolution:

INNER[(A<>)($Y)
    $A
    :
    K[Y<K>]
    L[Y<L>]
]

and

INNER[(A<>)($Y)
    $A()
    :
    K[Y<K>]
    L[Y<L>]
]

While these look different syntactically, they represent two expression regimes that achieve functional sameness:

  • Implicit Invocation ($A or $A$B$C): Formed as an explicitly concatenated pure value name. It asserts the combined pure value into the area of resolution. Invocation of a matching definition occurs implicitly and conditionally as a natural consequence of flow-dependency resolution. If a matching pattern exists, it triggers automatically.
  • Explicit Invocation ($A() or $A$B$C()): The addition of () forms an association name. This signals an explicit invocation step—traditionally used when treating expression steps as functional or associative evaluations within conditional expressions.

Despite this expressional difference—whether you view the statement as pure value flow or active definition invocation—the underlying data-flow model resolves them identically.


Syntax That Expresses Intent

This is a recurring design principle throughout MatterScript.

Whenever possible, the syntax is designed to communicate intent rather than introduce additional semantics.

Whether adding commas to demarcate structure or choosing between $A and $A() to express a value versus an association, these choices do not alter the underlying computation.

They do not change the internal representation.

They do not create a different correspondence relationship.

They simply record how the programmer intended the name to be composed and evaluated.

The language remains simple.

The source code becomes clearer.

The parser receives an unambiguous description of the intended structure.


Beyond Boolean Logic

The examples in this chapter use Boolean values because they are easy to visualize.

The idea is far more general.

Names can be composed from arbitrary tokens.

A biochemical simulation might compose the names of interacting molecules.

A routing engine might compose geometric regions.

A protocol stack might compose message types and state identifiers.

Wherever information can be combined to express a relationship, MatterScript can compose a correspondence name.

The mechanism never changes.

Only the participating tokens do.


Example: Forming Water

Imagine a tiny region of space filled with tokens.

Initially it contains nothing more than individual atoms.

H   H   O

Each atom is simply a token occupying a place.

No central program scans the region asking,

“Can I make water yet?”

Instead, local transform rules describe what happens when compatible neighbors exist.

A simplified rule might be written conceptually as:

H,O,H -> H₂O

or in MatterScript style,

H,O,H[H2O]

The exact syntax isn’t important here.

The important point is that the three neighboring tokens form a correspondence name.

When the relationship

H,O,H

exists locally, the transform rule matches.

The three individual tokens disappear.

A new token appears.

H₂O

No search occurred.

No iteration occurred.

No scheduler was involved.

The relationship itself caused the transformation.


Building Larger Structures

Now imagine the region contains thousands of atoms.

H H H O C N O H H ...

Most neighboring groups never satisfy any transform rule.

They simply remain unchanged.

Whenever a valid neighborhood forms, however, it immediately transforms into a larger structure.

Those larger structures become tokens themselves.

Now new rules become possible.

H
O
H
↓

H₂O

Later,

Protein Fragment
+
H₂O
↓

Hydrated Protein

The process continues naturally.

Simple tokens become molecules.

Molecules become complexes.

Complexes become organelles.

Every level is expressed using exactly the same mechanism:

local relationships produce new local relationships.


Reaching Stability

Eventually the region reaches a point where no transform rules apply.

Nothing is waiting.

Nothing is looping.

Nothing is consuming CPU cycles.

The computation has simply reached equilibrium.

At that point, the completed structure may propagate naturally to another region of the system, much like a finished protein being transported to a cell membrane.

The computation has not terminated because an instruction pointer reached the end of a program.

It has terminated because there are no remaining causal relationships capable of producing change.


Why This Matters

This example hints at something much larger than chemistry.

Weather systems.

Crystal growth.

Traffic flow.

Biological development.

Galactic formation.

All of these systems evolve because local interactions continually produce new local interactions.

Today’s computers simulate them by repeatedly scanning enormous arrays to ask whether anything has changed.

MatterScript describes the relationships directly.

When a valid relationship exists, it transforms.

When none exist, nothing happens.

The computation is driven by causality rather than inspection.


Relationships Become Names

Name composition illustrates one of the central ideas of MatterScript.

Relationships are not discovered by searching memory.

They are not computed through chains of conditional statements.

They are expressed directly as correspondence.

Individual tokens arrive.

Their relationship forms a name.

That name identifies the next transformation.

From the programmer’s perspective, this is a remarkably natural way to describe computation.

Rather than writing logic that asks what should happen?, you simply describe the relationships that exist.

The language allows those relationships to resolve themselves.

Drafting in progress…

Pure Value Expressions

A pure value expression is a MatterScript expression in which computation is expressed entirely through unique symbols, places, associations, and value transform rules. There is no conventional sequence of operations and no requirement that an intermediate value be explicitly stored or passed from one operation to another.

The Boolean full-adder provides a useful example because the same logic can be represented in two fundamentally different ways.

In an ordinary value-oriented representation, an AND, OR, or NOT operation can terminate in a value transform rule: a formed name selects a rule, and that rule produces a value. A pure value expression goes one step further. The expression itself becomes a population of symbols whose relationships progressively form the names of applicable rules.

Same logic. Different resolution. A pure value expression does not execute a sequence of Boolean operations. It establishes a space of possible associations and allows the arriving symbols to resolve the expression.


From Circuit to Expression

Consider the binary full-adder. Its inputs are:

  • X
  • Y
  • CI — carry in

and its outputs are:

  • SUM
  • CARRY

A conventional circuit describes this in terms of gates and wires. MatterScript can instead map every association path in that circuit to a collection of unique symbols.

For example:

C means X = 0
D means X = 1

E means Y = 0
F means Y = 1

G means CI = 0
H means CI = 1

The important point is that the symbols are unique to their places.

The 0 associated with one path is not simply the same global 0 as every other occurrence of zero. It represents the particular condition associated with that particular place in the expression.

Thus:

C ≠ E

even though both may represent the logical value 0.

Likewise:

D ≠ F

even though both represent the logical value 1.

This distinction allows the topology of the computation to remain encoded in the names themselves.


One Wire, Two Symbols

Each association path can therefore be represented by two unique symbols:

             wire
              │
          ┌───┴───┐
          │       │
          A       B
          │       │
        unique   unique
        symbol   symbol

For the full-adder, this produces the initial fan-out relationships:

A[g,k,o]
B[h,l,p]
C[G,K,O]
D[H,L,P]
E[I,M,Q]
F[J,N,R]

These statements do not perform Boolean arithmetic.

They establish name correspondence.

For example:

A[g,k,o]

means that content arriving at A is simultaneously associated with g, k, and o.

The bracket list is therefore a compact representation of fan-out. It is equivalent in principle to expressing the same correspondence three times:

A[g]
A[k]
A[o]

The comma-separated form simply makes the relationship explicit and compact.


Value Transform Rules as Resolution Points

Once the symbols have been distributed through the expression, combinations of symbols begin to form names.

For example:

G,I[S]
G,J[T]
H,I[S]
H,J[S]

These relationships define the next stage of resolution.

A pair of symbols such as:

G + I

can form the name:

GI

and the association:

GI[S]

states that the resulting relationship resolves toward S.

The same process continues through the network:

K,M[U]
K,N[U]
L,M[V]
L,N[U]

O,Q[W]
O,R[W]
P,Q[W]
P,R[X]

The resulting expression is not a list of instructions such as:

calculate this
then calculate that
then send the result here

Instead, it establishes a set of possible relationships. When the appropriate symbols coexist at a resolution place, the corresponding name becomes complete and its association becomes available.


The Shaking-Bag Model

This is easiest to understand using the shaking bag model.

Imagine that the place of resolution is a bag containing symbols. The bag is repeatedly populated as associations propagate through the expression.

The initial input symbols might produce a population such as:

B  F  C

After applying the first set of associations, the next population contains additional symbols:

R  C  P  G  J  K  N

Further resolution produces another population:

G  J  R  W  P  T  U

The important observation is that not every possible combination is meaningful.

Only certain combinations form the names of value transform rules.

For example:

G,J[T]
OR[W]
KN[U]

identify particular combinations that become complete within that population.

The resolution process therefore resembles a search through populations of symbols rather than a conventional instruction stream.

Resolution progression

Input population
      │
      │  associations
      ▼
New population
      │
      │  associations
      ▼
New population
      │
      │  associations
      ▼
...
      │
      ▼
Output symbols

The symbols that survive and propagate are determined by the structure of the expression itself.


From the First Half-Adder to the Second

The first portion of the full-adder resolves into the intermediate symbols:

S
T
U
V
W
X

These symbols then become the input population for the next stage:

S,U[a,c,e]
S,V[b,d,f]
T,U[b,d,f]
T,V[b,d,f]

Again, these are associations rather than conventional assignments.

The result of one resolution stage becomes the population from which the next set of names can form.

The subsequent stages continue this process:

g,a[i]
g,b[j]
h,a[i]
h,b[i]

k,c[m]
k,d[m]
l,c[n]
l,d[m]

o,e[q]
o,f[q]
p,e[q]
p,f[r]

and finally:

i,m[s]
i,n[t]
j,m[t]
j,n[t]

q,W[u]
q,X[v]
r,W[v]
r,X[v]

The entire Boolean full-adder has therefore been transformed into a network of symbolic associations.


The Pure Value Expression

The complete MatterScript expression is:

FULLADD($A,$B,$C)(<> CARRYOUT<>)

FULLADD[(X<>Y<>CI<>)( $SUM $CARRY)
  $X $Y $CI:

  //"fan-out input symbols"
  A[g,k,o]
  B[h,l,p]
  C[G,K,O]
  D[H,L,P]
  E[I,M,Q]
  F[J,N,R]

  //"define combinational resolution stages"
  G,I[S]
  G,J[T]
  H,I[S]
  H,J[S]

  K,M[U]
  K,N[U]
  L,M[V]
  L,N[U]

  O,Q[W]
  O,R[W]
  P,Q[W]
  P,R[X]

  //"fan out input to second half-adder"
  S,U[a,c,e]
  S,V[b,d,f]
  T,U[b,d,f]
  T,V[b,d,f]

  g,a[i]
  g,b[j]
  h,a[i]
  h,b[i]

  k,c[m]
  k,d[m]
  l,c[n]
  l,d[m]

  o,e[q]
  o,f[q]
  p,e[q]
  p,f[r]

  //"sum"
  i,m[s]
  i,n[t]
  j,m[t]
  j,n[t]

  //"carry"
  q,W[u]
  q,X[v]
  r,W[v]
  r,X[v]

  //"output"
  s[SUM<s>]
  t[SUM<t>]
  u[CARRY<u>]
  v[CARRY<v>]
]

Notice what is absent.

There is no expression such as:

SUM = X XOR Y XOR CI

There is no procedural sequence:

a = ...
b = ...
c = ...

There are no explicit intermediate Boolean values.

Instead, the program describes which symbols can associate with which other symbols.


No Invocation Is Required

This distinction is particularly important in MatterScript.

A conventional invocation might explicitly name a computational operation:

some_gate(input1, input2)

A pure value expression does not require that form.

The input content is simply introduced into the place of resolution:

$X $Y $CI:

From there, the content begins forming names through the associations defined in the expression.

The expression is therefore closer to a declarative topology of possible resolution than to an imperative program.

The computational behavior emerges from the relationships.


Final Symbols Become Output Associations

At the end of the resolution process, the surviving symbols are:

s
t
u
v

These are associated with the external result places:

s[SUM<s>]
t[SUM<t>]

u[CARRY<u>]
v[CARRY<v>]

The <...> notation identifies the destination place and the corresponding content.

Conceptually:

             ┌── SUM
s ───────────┤
t ───────────┘

             ┌── CARRY
u ───────────┤
v ───────────┘

In the shaking-bag model, these final symbols can be thought of as causing the destination definition to open: the appropriate destination association becomes complete and the resulting content can propagate outward.


Pure Values Versus Value Transforms

It is useful to distinguish two ways an expression can terminate.

Value-transform resolution

A formed name identifies a value transform rule:

symbols
   ↓
formed name
   ↓
value transform
   ↓
value

This is the model used by the earlier full-adder example.

Pure association resolution

A formed name identifies an association:

symbols
   ↓
formed name
   ↓
association
   ↓
new symbol/place

The second form does not require the computation to terminate in a conventional stored value.

This distinction is fundamental to MatterScript because the same logical function can be represented by different kinds of resolution.

A Boolean full-adder can therefore be expressed using explicit value transforms, or it can be expressed as a network of primitive symbolic relationships.


Why This Matters for MatterScript

Pure value expressions demonstrate an important property of the MatterScript model:

Computation does not have to be represented as the transformation of values. It can be represented as the formation and propagation of relationships.

Once computation is expressed in this form, several familiar distinctions begin to collapse.

The boundary between:

  • data and connectivity,
  • value and identity,
  • computation and wiring,
  • expression and topology,
  • software and hardware,

becomes much less rigid.

The expression already contains the connectivity information that a hardware implementation requires.

For example:

A[g,k,o]

is simultaneously:

  1. a symbolic relationship,
  2. a fan-out description,
  3. a piece of computational topology, and
  4. a potential hardware wiring relationship.

The MatterScript compiler can therefore lower the same semantic structure into different physical representations without requiring the programmer to rewrite the computation as a conventional hardware description.


Pure Value Expressions and Physical Computation

This is particularly significant for asynchronous and spatial computation.

A conventional program generally describes what operation happens next.

A pure value expression describes what relationships are possible where.

That distinction becomes increasingly useful as computation moves toward:

  • asynchronous logic,
  • NULL Convention Logic,
  • spatially distributed computation,
  • FPGA fabrics,
  • hardware synthesis,
  • sensor networks,
  • edge computation, and
  • physically embedded computation.

In these systems, locality and connectivity are not merely implementation details. They are part of the computational structure.

Pure value expressions provide a language-level mechanism for expressing that structure directly.


A Useful Mental Model

When reading a pure value expression, resist the temptation to read it as a sequence of assignments.

Instead, read it as a field of possible associations.

             symbols arrive
                    │
                    ▼
          ┌──────────────────┐
          │ resolution place │
          │                  │
          │   B  F  C        │
          │      G  J        │
          │   P       K      │
          └──────────────────┘
                    │
             names become
               complete
                    │
                    ▼
             new associations
                    │
                    ▼
          next resolution place

The expression is not telling the runtime to execute each line.

It is defining the relationships that allow symbols to propagate and form complete names.

That is why the full-adder can be described without an explicit chain of Boolean operations.


Summary

A pure value expression:

  • represents computation through unique symbols and associations;
  • assigns distinct symbols to the distinct paths of a computation;
  • allows symbols to propagate through name correspondence;
  • forms names when the required symbols coexist;
  • uses value transform rules or associations as resolution points;
  • progresses through populations of symbols rather than a procedural instruction sequence;
  • can express fan-out directly through constructs such as A[g,k,o];
  • can terminate in destination associations such as s[SUM<s>]; and
  • preserves the topology of the computation as part of the expression itself.

The Boolean full-adder demonstrates the central idea:

The logic does not need to be described as a sequence of value transformations. The relationships that make the logic possible can themselves constitute the expression.

This is one of the steps by which MatterScript moves from describing what a computation calculates toward describing where computation exists, what can associate with what, and how information propagates through physical structure.

Mutexes, Arbitration and Bundling

Bundles, Mutexes, and Arbitration

As Matterscript expressions become larger, it quickly becomes impractical to treat every path as an isolated signal. A single value may be represented by multiple rails, a bus may consist of dozens of paths, and multiple producers may need to communicate through a shared destination. Matterscript provides three closely related mechanisms to address these common patterns:

  • Bundles group multiple places into a single logical place.
  • Mutexes express that only one place in a group may contain content at a time.
  • Arbitration coordinates the flow of multiple independent sources into a single destination.

Together, these constructs allow complex dataflows to be expressed without sacrificing the explicit association model that lies at the heart of the language.

Bundles

A bundle is simply a collection of places that travel together as a single logical unit.

Rather than exposing every constituent place at every level of composition, a bundle allows an expression to present a cleaner interface while preserving the detailed structure internally.

For example, a dual-rail Boolean value consists of two mutually exclusive rails. At a higher level of composition it is often more convenient to treat those two rails as a single value.

OR($A $B)(Y<>)

Internally, however, the Boolean function still operates on the individual rails.

OR[( [{A0<> A1<>}] [{B0<> B1<>}] ) ( [{$0 $1}] )
     1< 3of6($A1 $A1 $A0 $B0 $B1 $B1) >
     0< 2of2($A0 $B0) > : ]

The two bracketed groups in the definition source list unbundle the invocation places $A and $B into their individual rails.

Likewise, the destination rails $0 and $1 are bundled back together and associated with the single invocation place Y<>.

This allows the implementation to operate on the explicit physical representation while the surrounding design works with a more convenient logical representation.

Arity of source lists

The number of top-level entries in the invocation’s source list must equal the number of top-level entries in the definition’s source list. They correspond positionally: the first invocation place is bound to the first definition source, the second to the second, and so on.

A single invocation place may bind to a bracketed group of definition members — that is the unbundling case. But it may not bind to multiple top-level sources. Conversely, a bracket pair in the invocation may not be flattened to multiple definition sources. Arity is measured at the top level, not after unbundling.

For example, OR($A $B) has two top-level source entries. The definition OR[([{A0<> A1<>}] [{B0<> B1<>}])...] also has two top-level source entries: [{A0<> A1<>}] and [{B0<> B1<>}]. The correspondence is 1-to-1 even though each side expands to a different number of physical rails when flattened.

Where bundle member names live

When a bundle is unbundled, the member names come from the definition’s source list, not from the invocation. An invocation that writes OR($A $B) and a definition that writes OR[([{A0<> A1<>}] [{B0<> B1<>}])...] binds $A to the group {A0, A1} and $B to the group {B0, B1}. The names A0, A1, B0, B1 are canonical; they are what the emitted hardware uses for ports and signals. The invocation’s $A and $B are labels for the association, not names in the physical interface.

When the compiler emits a top-level network entity for such an invocation, the entity’s input ports are named after the definition’s member names. The invocation’s bundle labels do not appear in the port list.

Bundles as structure

Conceptually, bundling is similar to a structure or record in conventional programming languages, except that the bundle represents paths of association rather than fields of memory.

Bundles are not limited to dual-rail logic. They can represent any collection of places that naturally travel together.

A four-digit number, for example, might be represented internally as:

A0<> A1<> A2<> A3<>

while appearing externally as simply:

A<>

Each level of composition may bundle or unbundle information as appropriate for its level of abstraction.

Nested Bundles

Bundles may themselves contain other bundles.

Each additional level of bundling adds another outer pair of brackets.

For example:

[A0 A1]

represents a single bundle.

[[A0 A1] [B0 B1]]

represents two bundles grouped into a larger bundle.

When content flows into a definition, each level of unbundling removes one level of brackets until the expression reaches the individual places it operates upon.

Because bundling follows the natural hierarchy of composition, large interfaces remain compact while definitions retain complete visibility of their internal structure.

Mutual Exclusion (Mutex)

Many bundled representations describe values for which only one member may ever be active.

Dual-rail logic is the simplest example.

If a Boolean value is represented by two rails:

A0<>
A1<>

then only one rail may contain content at any instant.

The rails are therefore enclosed in braces:

{A0<> A1<>}

The braces declare a mutual exclusion relationship.

Rather than describing computation, the mutex expresses a property of the representation itself.

It tells the reader—and the compiler—that these places are alternative representations of the same logical value.

Mutexes appear naturally throughout Matterscript:

  • dual-rail values
  • multi-rail encodings
  • one-hot state machines
  • alternative symbolic representations

They communicate intent while allowing the compiler to verify that mutually exclusive paths remain mutually exclusive throughout the design.

Mutex and completeness

A mutex group participates in completeness differently than a bundle does.

A bundle’s completeness is the conjunction of its members: the group is complete when every member has content.

A mutex group’s completeness is the disjunction of its members: the group is complete when at least one member has content, and the mutual exclusion property guarantees that at most one does. “Complete” for a mutex therefore means “exactly one member has content.”

This distinction matters for source and destination lists. Consider a definition whose source list contains two mutex groups:

OR[({A0<> A1<>}{B0<> B1<>})({$0 $1})
  :
  A0,B0[0]
  A0,B1[1]
  A1,B0[1]
  A1,B1[1]
]

(Example 12.9, Mutually exclusive completeness.)

The source list ({A0<> A1<>}{B0<> B1<>}) names two mutex groups. The definition is complete when the first group has exactly one of A0/A1, and the second group has exactly one of B0/B1. No other combination counts as complete — an empty group means the source is still waiting for content.

The destination list ({$0 $1}) is also a mutex group. The definition’s output is a value on exactly one of $0 or $1, never both. Which one is determined by the resolution rules.

Each rule reads a completeness pattern from the sources and asserts a value on the corresponding destination:

Sources presentDestination
A0, B00
A0, B11
A1, B01
A1, B11

The 0 and 1 in the rules are tokens, not destination names. Because the destination list is a mutex group of places named $0 and $1, and the tokens asserted by the rules are 0 and 1, the mutex acts as the routing mechanism: asserting token 0 means “activate $0”, asserting token 1 means “activate $1”. Only one rule fires per wavefront, so only one destination place receives content — exactly what the mutex declares.

Completeness by group kind

The two kinds of group have different completeness conditions on sources and destinations:

Group kindSource completenessDestination completeness
Bundle [...]all members presentall members asserted
Mutex {...}exactly one member presentexactly one member asserted
Arbitration {{...}}at least one member present(not a destination form)

How mutexes affect emitted hardware

Mutually exclusive destinations are wired as mutually exclusive drivers. When the compiler emits VHDL for the example above, each destination place becomes an output signal driven by a conditional assignment:

ms_0 <= data_value(0) when (A0_valid = '1' and B0_valid = '1') else null_value;
ms_1 <= data_value(1) when (A0_valid = '1' and B1_valid = '1')
                       or (A1_valid = '1' and B0_valid = '1')
                       or (A1_valid = '1' and B1_valid = '1')
             else null_value;

Each destination’s assignment falls back to null_value when its condition doesn’t hold, so the two signals are never both driven in the same cycle. That is the mutex property, enforced structurally by the emitted code rather than by convention.

Design consequences

The mutex is not a notation for exclusive-or logic, and it is not a shorthand for a decoder. It is a statement about the representation: these places are alternative encodings of a single logical value, and content appearing in more than one of them would be a contradiction.

Because it’s a property of the representation rather than a computation, the mutex propagates through composition. A definition that consumes a mutex group and produces another mutex group has preserved the property end-to-end. Composing two such definitions preserves it again. The compiler can therefore reason about mutexes at every level of a design without having to re-derive the property at each layer.

This is also why mutexes and bundles are not interchangeable: a bundle asserts “all members together,” a mutex asserts “exactly one member.” Using the wrong one describes a different representation, and the emitted hardware follows.

Arbitration

Mutual exclusion describes values.

Arbitration describes behavior.

Consider two independent producers attempting to send content to the same destination.

If both produce content simultaneously, which one should proceed first?

In conventional software this often becomes a lock, semaphore, message queue, or scheduler.

In Matterscript, arbitration is expressed directly as part of the association graph.

Arbiter({{$place1 $place2}})(next<>)

Arbiter[(placeB<>)(pass<>)
    pass<$placeB> :
]

The double braces in the invocation indicate that $place1 and $place2 are competing for access to the same flow path.

If both places contain content simultaneously, the arbiter performs a competition.

The winner proceeds through the expression.

The remaining content simply waits until the next opportunity.

Importantly, the language does not define which competitor wins.

The result is intentionally nondeterministic.

If only one place contains content, no competition occurs and the content flows immediately.

The effect is to transform multiple independent, uncoordinated flows into a single coordinated flow.

place1  ─┐
         │
place2 ──┼──► Arbiter ───► next<>
         │
place3 ──┘

Arbitration therefore provides the synchronization point between concurrent producers while preserving Matterscript’s declarative association model.

The {{...}} group is a single source

The double braces {{...}} denote a single arbitration group. Unlike nested bundles, which use [[...] [...]] to mean “a bundle of bundles,” the doubled braces are one grouping construct: an arbitration group.

An arbitration group with two members counts as one source entry in the invocation’s source list, not two. It participates in arity the same way a bracket group does: as a single top-level source that expands internally into its members.

Arbitration semantics

An arbitration group at a source means: content may arrive on any subset of the members, and the definition receives exactly one wavefront per accepted arrival. If two members arrive in the same wavefront, one is chosen nondeterministically and the other waits for the next cycle. The definition body sees one wavefront at a time, never both competitors simultaneously.

At the destination, the arbiter’s output is a single stream of wavefronts, one per accepted source arrival. There is no merging of contents, no queueing at the destination, and no priority — just serialization.

The arbitration property lives at the invocation level. The definition that receives the arbitration group sees a flat single source and processes one wavefront at a time; it does not need to know how many competitors there are.

Grammar of grouping brackets

Three bracketing forms exist, each a single syntactic construct:

group  ::= "[" place* "]"     -- bundle
         | "{" place* "}"     -- mutex
         | "{{" place* "}}"   -- arbitration

Each counts as exactly one entry in the surrounding list. Nesting uses additional bracket pairs: [[...] [...]] is a bundle of bundles, [{{...}} {{...}}] is a bundle of arbiters, and so on.

A Hierarchy of Flow

Bundles, mutexes, and arbitration each solve a different problem.

Bundles organize related paths.

Mutexes describe alternative representations of the same logical value.

Arbitration coordinates independent flows that converge upon a common destination.

Although distinct, they frequently appear together.

A bundled value may contain mutually exclusive rails.

Multiple bundled values may compete through an arbiter.

An arbiter may produce another bundled value for the next stage of computation.

These constructs allow Matterscript to scale naturally from individual signal paths to complex computational fabrics while preserving the same fundamental principle introduced throughout this guide:

computation is expressed as the movement and transformation of associations rather than the execution of sequential instructions.

Generate Blocks

One of MatterScript’s goals is to make large computational networks as easy to describe as small ones.

That creates an obvious challenge.

Many real systems consist of thousands—or millions—of nearly identical relationships.

A memory array contains thousands of repeated cells.

A systolic array repeats the same processing element over and over.

A cellular automaton may contain millions of identical neighborhoods.

Writing these structures by hand would add no information to the program. It would merely repeat the same pattern over and over.

The generate block exists to solve that problem.

Rather than describing computation, it describes how to construct computation.


Generating Relationships

A generate block contains a generator expression followed by the ranges over which that expression should be expanded.

// Bind to a 2D spatial domain with bounds
@domain(spatial2d, size: [300, 500])

@generate {
  // Format: [ Left , Center , Right ] -> New Center State
  [2, 1, 5]:4
  [3, 2, 4]:6
  [4, 4, 6]:4
}

Instead of manually writing hundreds or thousands of nearly identical association expressions, the programmer describes the pattern once.

The compiler expands that pattern into the corresponding MatterScript network.

The generated result is exactly the same as if the programmer had written every relationship by hand.


Source Generation, Not Runtime Execution

The most important thing to understand is that generate is not part of the running computation.

Nothing is generated while the program executes.

The generate block exists only during compilation.

Its purpose is to produce ordinary MatterScript definitions, places, invocations, and transform rules.

Once expansion is complete, the generated code behaves exactly like handwritten MatterScript.

This keeps the language remarkably simple.

The computational model never changes.

The compiler merely helps construct larger expressions.


Declaring the Input Space

The inputs section specifies the variables that participate in generation.

inputs $x[0..255], $y[0..255]

Each variable defines a finite range over which the generator expression will be expanded.

Rather than describing individual values, these declarations describe an entire space of possible relationships.

The generator expression can then refer to those variables symbolically.


Describing the Output Space

The output declaration specifies the range of values that the generated relationships may produce.

output [0..512]

This gives the compiler enough information to construct the resulting association network while preserving the same correspondence model used throughout the language.


Compile-Time Constants

Large generated structures often depend on parameters.

The optional const declarations allow those parameters to be named.

const MAX_LIMIT = 512
const OFFSET = 4

These constants exist only while the generator is expanding the network.

They are not runtime variables.

Like the rest of the generate block, they disappear once generation is complete.


Scaling Without Changing the Language

One of the remarkable aspects of MatterScript is that the generated result uses exactly the same language as everything else.

The compiler does not introduce a hidden runtime.

It does not create a new execution model.

It simply writes more MatterScript.

Whether a definition contains ten relationships or ten million, the underlying computational model remains identical.

Generation changes the size of the description.

It does not change the nature of the computation.


From Patterns to Computational Fabric

Today, generate provides a concise way to construct large regular networks.

In the future, it may become the foundation for much richer forms of synthesis.

A geometry engine could generate millions of spatial relationships from a compact geometric description.

A compiler could synthesize entire arithmetic units from algebraic specifications.

A simulation framework could construct vast lattices of interacting cells from a single neighborhood rule.

In every case, the principle remains the same.

The programmer describes the pattern.

The compiler expands the pattern into explicit relationships.

Those relationships become part of the computational fabric, indistinguishable from code written by hand.

Drafting in progress…

Domains & Spatial Contexts

In MatterScript, computation is inherently spatial. Rather than treating physical coordinates or hardware topology as secondary metadata, MatterScript uses the @domain directive to declare the geometric context in which signals propagate.

Although the @domain(...) syntax remains consistent across definitions, its semantics bifurcate cleanly based on dimensionality. A domain can act either as a discrete grid for bounded state generation or as a continuous topological graph for physical hardware routing.


1. Discrete Stencil Domains (spatial2d)

A spatial2d domain establishes a discrete, bounded coordinate space. It is typically paired with cellular automata, stencil kernels, or finite-difference solvers where state updates execute across a grid of uniform spatial registers.

Key Mechanics

  • Explicit Bounding: Defines rigid bounding boxes via properties such as size: [Width, Height].
  • State Generation: Coordinates input state tuples across localized neighborhood windows or coordinate offsets.
  • Boundary Conditions: Standardizes edge-wrapping rules (such as toroidal grids or clamped edges) at the domain boundaries.

Compiler Role

When encountering a spatial2d domain, the compiler lowers the definitions into grid memory allocations, compute shaders (such as WGSL), or flattened two-dimensional arrays.

// Bind to a 2D spatial domain with explicit bounds
@domain(spatial2d, size: [300, 500])

@generate {
  // Format: [ Left , Center , Right ] -> New Center State
  [2, 1, 5] : 4
  [3, 2, 4] : 6
  [4, 4, 6] : 4
}


2. Topological Graph Domains (spatial3d)

In contrast to discrete grids, a spatial3d domain does not represent an array to iterate across. Instead, it serves as a compiler hint that geometric primitives (point, edge, loop, face) describe static spatial adjacency and physical routing proximity.

Rather than relying on static polygon meshes or imperative sweep pipelines, physical domains are expressed using topological building blocks. Because spatial proximity directly dictates computational interaction, these relationships compile into the exact same propagation graph as time—turning geometric boundary conditions directly into executable hardware and software realizations.

Software Reference & Primitive Vocabulary

The spatial3d domain exposes a rich set of structural keywords, predicates, CSG operations, and spatial transforms:

Structural Primitives

  • Point (point): A discrete physical location within space defined via point(x, y, z).
  • Edge (edge): A directional, causal connection established between two points using edge(pointA, pointB).
  • Loop (loop): An ordered, closed sequence of connected edges defining a spatial boundary via loop(edges).
  • Face (face): A surface domain created by enclosing one or more loops via face([edge]).
  • Volume (volume): Enclosed, three-dimensional spatial regions bounded by defined faces.
  • Group (group): A hierarchical composition that organizes geometric relationships into nested, modular definitions.

Spatial Predicates & Proximity

  • near(faceA, faceB): Evaluates spatial proximity between surface domains.
  • adjacent(edgeA, edgeB): Asserts boundary connectivity between neighboring edges.
  • contains(point): Determines enclosure of a point within a spatial volume or boundary.
  • intersects(): Computes structural overlap between spatial elements.
  • touches(): Checks boundary contact without volumetric overlap.
  • within(distance): Evaluates whether elements fall within a specified metric radius.

Constructive & Swept Geometry

  • extrude(face, height): Projects a planar face along a normal vector to form a 3D volume.
  • sweep(profile, path): Sweeps a closed profile along a continuous path.
  • loft(loop1, loop2): Interpolates a continuous topological surface between disparate boundary loops.
  • revolve(profile, axis): Rotates a 2D profile around an axis of rotation.

CSG & Boolean Operations

  • union(): Combines multiple spatial regions into a unified domain.
  • difference(): Subtracts one spatial boundary from another.
  • intersection(): Resolves the common volumetric domain shared between shapes.

Spatial Transformations & Meshing

  • subdivide(): Refines topological density across a domain.
  • mirror(): Reflects geometry across a specified plane.
  • translate() / rotate() / scale(): Shifts, rotates, or resizes spatial geometries across coordinate axes.

Topological Declaration vs. Matrix Transformation

A major advantage of graph-based topological modeling is that non-orientable 3D manifolds do not require continuous mathematical matrix transformations inside the AST.

Consider a Möbius ribbon. In traditional graphics pipelines, this shape requires procedural surface integration or continuous rotation matrices. In MatterScript, it is expressed simply by defining four quad faces where the topological boundary of the final face twists its edge bindings:

mobius_strip[()($f0, $f1, $f2, $f3) 
    @domain(spatial3d)
    // 1. Instantiating internal point sources (p0..p7)
    p0< point(1.0, 0.0, -0.2) >
    p1< point(1.0, 0.0, 0.2) >
    p2< point(0.0, 1.0, -0.2) >
    p3< point(0.0, 0.2, 0.0) >
    p4< point(-1.0, 0.0, -0.2) >
    p5< point(-1.0, 0.0, 0.2) >
    p6< point(0.0, -1.0, 0.0) >
    p7< point(0.0, -0.2, -0.2) >

    // 2. Consuming point destinations ($pX) to drive exported face destinations (fX<>)
    f0<face(loop(edge($p0, $p1), edge($p1, $p3), edge($p3, $p2), edge($p2, $p0)))>
    f1<face(loop(edge($p2, $p3), edge($p3, $p5), edge($p5, $p4), edge($p4, $p2)))>
    f2<face(loop(edge($p4, $p5), edge($p5, $p7), edge($p7, $p6), edge($p6, $p4)))>
    f3<face(loop(edge($p6, $p7), edge($p7, $p0), edge($p0, $p1), edge($p1, $p6)))>
    :
]

The twist does not emerge from an imperative sweep command or a runtime matrix evaluation. It exists because the causal connectivity graph swaps its terminal bindings—connecting edge($p0, $p1) back to edge($p1, $p6) at the boundary. The geometric surface is a direct consequence of topological declaration.


Binding Execution to Spatial Domains

An invocation targeting a geometric definition operates by binding dynamic execution and dataflow to the spatial boundary. It provides a placement vector (translation, orientation, scaling) to ground that definition within a concrete coordinate system.

Because the geometric definition specifies local manifold adjacencies, the invocation supplies the transform that projects those local spatial registers directly into global compute space or physical hardware placement constraints.

Integer Quantization

Most programming languages begin with numbers.

MatterScript begins with distinctions.

That difference may seem subtle at first, but it changes the way we model problems.

Traditional software assumes that every quantity is fundamentally a number.

MatterScript asks a different question:

What are the meaningful states of this system?

The answer is often not a number at all.

It is a finite set of distinguishable possibilities.

Those possibilities become single-digit numbers.


Single-Digit Numbers

When most programmers hear the word digit, they think of decimal notation.

MatterScript uses the term differently.

A single-digit number is a value represented by one symbol, regardless of how many possible values exist.

If a quantity can take on fifty distinct values, then its number system simply contains fifty digits.

There is no need to construct that value from multiple place-value digits.

Instead, each possible state is represented directly.

You can think of it as choosing a radix large enough that every value fits into a single position.


From Mathematics to Tokens

This changes one of the programmer’s primary responsibilities.

In conventional software, we begin by deciding how many bits are needed to store a number.

In MatterScript, we begin by deciding what distinctions actually exist.

Suppose we are modeling traffic lights.

A traditional program might write:

0 = Red
1 = Yellow
2 = Green

Those integers have no intrinsic meaning.

They are merely encodings.

MatterScript would instead treat the three possible states as the vocabulary of the system.

RED
YELLOW
GREEN

Each is a single token.

Each is equally fundamental.

None is “greater” than another.


There Is No Number Line

This is perhaps the hardest idea for programmers to internalize.

Single-digit numbers do not necessarily possess numerical relationships.

There is no requirement that one value be larger than another.

There may be no meaningful notion of distance.

No ordering.

No arithmetic.

Only distinction.

For a traffic light, asking whether GREEN is larger than RED is meaningless.

For a protein, asking whether Folded is greater than Unfolded is equally meaningless.

The values simply represent different states.


Quantizing a Weather System

Consider a simple weather simulation.

Instead of assigning floating-point temperatures everywhere, we might decide that, for our model, only a small number of atmospheric states matter.

COLD
COOL
WARM
HOT

Humidity might become

DRY
NORMAL
HUMID
SATURATED

Wind might become

NORTH
SOUTH
EAST
WEST
CALM

These are no longer numbers in the mathematical sense.

They are tokens describing the current state of a region.

Transform rules define how neighboring regions influence one another.

Millions of cells evolve simultaneously through local interactions.

The programmer has quantized the continuous world into a vocabulary appropriate for the simulation.


Quantizing Chemistry

Chemistry provides another example.

A conventional simulation often stores tables of atomic properties and repeatedly computes interactions numerically.

MatterScript encourages a different perspective.

The fundamental vocabulary might consist of tokens such as

H
O
C
N

along with bonding states

FREE
SINGLE_BOND
DOUBLE_BOND

Transform rules describe how neighboring atoms interact.

As relationships become satisfied, larger molecular structures emerge naturally from the network.

The simulation evolves because local distinctions change, not because a processor repeatedly scans an array looking for work.


Quantizing Digital Logic

Digital electronics has already been using this idea for decades.

A logic signal is not an arbitrary integer.

It has a very small vocabulary.

NULL
0
1

Each state has a specific meaning.

Transform rules describe how those states propagate through logic.

This is precisely why MatterScript maps so naturally onto asynchronous hardware.

The language speaks the same conceptual language as the underlying circuits.


Choosing the Vocabulary

Viewed this way, quantization becomes one of the most important design decisions in a MatterScript program.

Before writing transform rules, the programmer asks:

What distinctions matter?

Everything else is discarded.

A weather model does not need nanometer precision.

A biological model may not care about floating-point voltages.

A traffic simulation has no concept of molecular bonds.

Each domain chooses its own vocabulary.

The resulting tokens become the atoms from which the computation is built.


Precision Through Representation

At first glance, reducing continuous mathematics to a finite vocabulary might appear to sacrifice precision.

In practice, the opposite is often true.

Every simulation already makes approximations.

Floating-point numbers are approximations.

Finite element meshes are approximations.

Time steps are approximations.

MatterScript simply makes that approximation explicit.

Instead of hiding quantization inside binary encodings, it asks the programmer to define the meaningful distinctions directly.

Once those distinctions have been chosen, the resulting computation becomes dramatically simpler.

Relationships become direct.

Transform rules become local.

Hardware can exploit massive concurrency because every token already represents exactly the information the system needs.


Computation as Distinction

This chapter illustrates a recurring theme throughout MatterScript.

Traditional software tends to begin with mathematics and encode the world into numbers.

MatterScript begins with the world itself.

It asks what objects exist, what states they may occupy, and how those states transform through local interactions.

Numbers become just one possible vocabulary among many.

The real foundation of computation is not arithmetic.

It is distinction.

Drafting in progress…

The Runtime Boundary

One of the central ideas behind MatterScript is that a program should describe what a computation is, not how it executes.

Most programming languages blur these two concerns together. The language describes the computation, but it also implicitly assumes a particular execution environment: a processor with an instruction pointer, a stack, a heap, and a runtime responsible for coordinating them all.

MatterScript makes a different distinction.

The language ends where the relationships have been fully described.

Everything beyond that point belongs to the compiler.


Programs Describe Relationships

When you write a MatterScript definition, you are not writing instructions for a processor.

You are describing a network of causal relationships.

FULLADD($A $B $C)(SUM<> CARRY<>)

The definition says nothing about clocks.

Nothing about memory.

Nothing about registers.

Nothing about threads.

Nothing about instruction order.

It simply declares that when the appropriate information arrives, these relationships exist.

That description is complete in itself.


From Source to Association Graph

The compiler’s first responsibility is not to generate machine code.

It is to recover the computational fabric described by the program.

Conceptually, every MatterScript program is transformed into an association graph.

MatterScript Source
        │
        ▼
Association Graph

This graph contains every place, every invocation, every correspondence relationship, and every causal dependency expressed by the source.

At this point the computation has already been defined.

Nothing about processors or hardware has entered the picture.

The graph is simply a faithful representation of the relationships described by the programmer.


The Runtime Boundary

The association graph forms the boundary between the language and its implementation.

Everything above this boundary belongs to MatterScript.

Everything below it belongs to the compiler.

MatterScript Source
        │
        ▼
Association Graph
        │
────────┼────────  Runtime Boundary
        │
        ▼
Backend Mapping
        │
 ┌──────┼──────────────┐
 ▼      ▼              ▼
FPGA   POSIX         GPU
VHDL   C/Pthreads    CUDA

The language never specifies how those relationships should be realized.

That decision belongs entirely to the backend.


One Program, Many Realizations

This separation allows the same MatterScript program to target radically different computational substrates.

One backend may generate VHDL, mapping every relationship onto lookup tables, routing resources, and asynchronous logic.

Another may generate a POSIX application, where independent regions of the graph execute as cooperating threads.

A GPU backend may map repeated structures onto thousands of parallel execution units.

Future backends might target custom ASICs, distributed systems, neuromorphic hardware, or architectures that have not yet been invented.

The program itself does not change.

Only its realization changes.


The Runtime Is an Implementation Detail

Traditional software is usually thought of in three layers:

Program
    │
    ▼
Runtime
    │
    ▼
Hardware

The runtime is responsible for scheduling execution, managing memory, coordinating threads, and providing operating-system services.

MatterScript shifts that boundary.

Program
    │
    ▼
Association Graph
    │
    ▼
Physical Realization

There is no mandatory runtime.

There is no required virtual machine.

No required scheduler.

No required heap.

No required stack.

A particular backend may choose to implement some or all of these mechanisms, but they are no longer properties of the language itself.

They are engineering decisions made during realization.


Computation Becomes Portable

This separation has another consequence that is unlike anything possible in traditional software.

Today’s computers routinely transmit data across networks.

Sometimes they transmit executable programs.

But those programs are still written for a particular processor architecture and operating system. They assume the receiving machine will execute instructions using its own runtime.

MatterScript can transmit something much more fundamental.

It can transmit the computation itself.

Imagine a running MatterScript system that constructs a new computational network dynamically—a signal-processing pipeline, a robotic controller, a physics simulation, or a specialized accelerator. That generated network is simply another MatterScript description. It can be handed to the local backend for synthesis, becoming dedicated hardware inside an FPGA.

Or it can be sent across the network to an entirely different machine.

That receiving machine might have different processors.

Different memory.

Different accelerators.

Different hardware capabilities.

It compiles the received MatterScript into a realization appropriate for its own architecture while preserving exactly the same causal relationships.

In effect, the computation has moved without carrying any assumptions about the machine on which it will execute.

One computer has transmitted part of itself to another.

Not merely its data.

Not merely its executable binary.

Its computational structure.

It is, in a very real sense, the teleportation of machinery.


Preserving the Relationships

The compiler’s job is not simply to translate syntax.

Its responsibility is to preserve the causal structure expressed by the program.

Whether that structure becomes a network of FPGA logic cells, a collection of POSIX threads, or a GPU kernel, the observable behavior must remain identical.

Only the implementation strategy changes.

The relationships do not.

This is the same principle that has guided the language from the beginning.

MatterScript describes causality.

The compiler determines how that causality is embodied in a physical system.


A Language Independent of Its Machine

Perhaps the most significant consequence of the runtime boundary is philosophical rather than technical.

For decades, programming languages have largely been descriptions of how to drive a processor.

MatterScript is instead a description of a computation itself.

The processor is no longer the center of the programming model.

It is merely one possible realization of a deeper computational structure.

That distinction opens possibilities that have scarcely existed before. Computational structures can be generated, evolved, synthesized into hardware, or transmitted to entirely different machines for realization. Computation becomes something that can inhabit whatever physical substrate is available without changing its identity.

The language ends when the relationships have been described.

Everything beyond that point is an implementation choice.

Standard Library

Logic

Arithmetic

Geometry

Spatial transforms

Signal processing

Probability

Chemistry

Biology

Linear algebra

Cryptography

Networking

Simulation primitives

Collections

File formats

Temporal relationships

Drafting in progress…

Part VI: Geometry Becomes Code

Traditional languages separate computation from geometry.

MatterScript unifies them.

Meshes become executable.

CAD models become programs.

LIDAR scans become computational substrates.

This is where MatterScript begins to differ fundamentally from every previous programming language. The language’s geometry-aware placement model projects physical domains onto FPGA fabrics while preserving neighborhood relationships and compensating projection distortion with synthesized delays.

To achieve this, MatterScript utilizes domain selectors to activate domain-specific extensions directly within the structural graph. Introducing the spatial3d domain selector exposes a specialized set of topological primitives, geometric constructors, spatial predicates, and transformation operators tailored for 3D computational domains.

Below is the list of structural keywords and functions available under the spatial3d domain:

  • Point (point): Not merely a 3D coordinate vector, but a discrete physical location within space defined via point(x, y, z).
  • Edge (edge): A directional, causal connection established between two points using edge(pointA, pointB).
  • Loop (loop): An ordered, closed sequence of connected edges defining a spatial boundary via loop(edges).
  • Face (face): A surface domain created by enclosing one or more loops via face([edge]).
  • Volume (volume): Enclosed, three-dimensional spatial regions bounded by defined faces.
  • Group (group): A hierarchical composition that organizes geometric relationships into nested, modular definitions.

Spatial Predicates & Proximity

  • near(faceA, faceB): Evaluates spatial proximity between surface domains.
  • adjacent(edgeA, edgeB): Asserts boundary connectivity between neighboring edges.
  • contains(point): Determines enclosure of a point within a spatial volume or boundary.
  • intersects(): Computes structural overlap between spatial elements.
  • touches(): Checks boundary contact without volumetric overlap.
  • within(distance): Evaluates whether elements fall within a specified metric radius.

Constructive & Swept Geometry

  • extrude(face, height): Projects a planar face along a normal vector to form a 3D volume.
  • sweep(profile, path): Sweeps a closed profile along a continuous path.
  • loft(loop1, loop2): Interpolates a continuous topological surface between disparate boundary loops.
  • revolve(profile, axis): Rotates a 2D profile around an axis of rotation.

CSG & Boolean Operations

  • union(): Combines multiple spatial regions into a unified domain.
  • difference(): Subtracts one spatial boundary from another.
  • intersection(): Resolves the common volumetric domain shared between shapes.

Spatial Transformations & Meshing

  • subdivide(): Refines topological density across a domain.
  • mirror(): Reflects geometry across a specified plane.
  • translate(): Shifts spatial entities across coordinate axes.
  • rotate(): Applies rotational transformations around a specified axis.
  • scale(): Uniformly or non-uniformly resizes spatial geometries.

By expressing physical domains using these topological building blocks rather than static polygon meshes, spatial proximity directly dictates computational interaction. Spatial relationships compile into the exact same propagation graph as time, turning geometric boundary conditions directly into executable hardware and software realizations.

Consider how this relationship graph handles non-orientable 3D manifolds without requiring mathematical matrix transformations inside the AST. A Möbius ribbon can be expressed by defining four quad faces where the topological boundary of the final face twists its edge bindings:

mobius_strip[()($f0, $f1, $f2, $f3) 
    spatial3d :
    // 1. Instantiating internal point sources (p0..p7)
    p0< point(1.0, 0.0, -0.2) >
    p1< point(1.0, 0.0, 0.2) >
    p2< point(0.0, 1.0, -0.2) >
    p3< point(0.0, 0.2, 0.0) >
    p4< point(-1.0, 0.0, -0.2) >
    p5< point(-1.0, 0.0, 0.2) >
    p6< point(0.0, -1.0, 0.0) >
    p7< point(0.0, -0.2, -0.2) >

    // 2. Consuming point destinations ($pX) to drive exported faces (fX<>)
    f0<face(loop(edge($p0, $p1), edge($p1, $p3), edge($p3, $p2), edge($p2, $p0)))>
    f1<face(loop(edge($p2, $p3), edge($p3, $p5), edge($p5, $p4), edge($p4, $p2)))>
    f2<face(loop(edge($p4, $p5), edge($p5, $p7), edge($p7, $p6), edge($p6, $p4)))>
    f3<face(loop(edge($p6, $p7), edge($p7, $p0), edge($p0, $p1), edge($p1, $p6)))>
]

The twist does not emerge from a continuous rotation matrix or an imperative sweep command. It exists because the causal connectivity graph swaps its terminal bindings (edge(p0,p1) versus edge(p1,p0)). The geometric surface is a direct consequence of topological declaration.

In IPL, an invocation targeting a geometric definition operates by binding dynamic execution/dataflow to the spatial boundary and providing a spatial placement vector (translation, orientation, scaling) to ground that definition within a coordinate system.

Because the geometric definition specifies relative topological adjacencies (the local manifold), the invocation supplies the transform that projects those local spatial registers into global compute space or hardware coordinates.


Invocation Syntax & Mechanics

Following the canonical IPL invocation model:

$$\text{invocationName}[(\text{destination_bindings})(\text{source_bindings}) \quad \text{spatial_domain} @ \text{placement_vector} : \text{execution_rules}]$$

  • Domain Target (spatial_domain): Specifies which spatial definition acts as the physical/topological substrate.
  • Placement Vector (@ placement_vector): Supplies the origin offset, orientation frame, or scale parameters to position the domain in 3D coordinate space.
  • Stream/Data Bindings ((destinations)(sources)): Feeds real-time computational inputs and extracts output streams across the placed geometry.

Conceptual Syntax Example

// Invocation instantiating computation inside the declared 3D domain at explicit coordinates
field_solver[(output_mesh)(sensor_stream) mobius_strip @ transform3d{ pos: [10.0, 0.0, 5.0], rot: [0.0, 45.0, 0.0], scale: 1.0 } :
    // Execution rules evaluated across the placed topological domain
    output_mesh = compute_wave_propagation(sensor_stream)
]


How the Compiler Resolves the Invocation

  1. Coordinate Transformation at AST Lowering: The compiler applies the invocation’s @ transform3d vector to the definition’s local point set (p0…p7), transforming local anchors into global physical/virtual 3D coordinates.
  2. Channel Delay Re-Synthesis: If the placement transformation stretches, scales, or skews the domain, the compiler recalculates edge physical lengths and re-synthesizes latency-compensation delays along the graph channels.
  3. Execution Domain Allocation:
  • For Hardware (FPGA/ASIC): The global coordinates directly map the graph node placement onto corresponding physical tiles or logic sectors.
  • For Software (SPIR-V/CPU): Global coordinates determine spatial partitioning, workgroup dispatch dimensions, and thread routing.

Manufacturing Outputs and Hardware Realization

Because a Möbius strip is a valid 2D manifold embedded in 3D space without self-intersection, it serves as a powerful bridge between abstract topology, physical manufacturing, and silicon compilation.

1. Physical Manufacturability Traditional 3D models often suffer from manifold errors, unclosed shells, or self-intersecting meshes when sent to CAD/CAM software or 3D printers. Because MatterScript defines geometry via explicit topological adjacencies (point, edge, loop, face), the output guarantees surface continuity. The resulting AST compiles cleanly into:

  • Toolpath Generation: Continuous CNC milling paths and 3D printer G-code without surface singularities.
  • Unfolded Flat Patterns: Direct projection into 2D sheet metal cutting profiles or flexible PCB layouts.

2. Flattening 3D Topologies to 2D FPGA Fabrics Mapping non-orientable 3D compute domains onto a flat 2D FPGA logic grid traditionally causes severe routing congestion. The IPL compiler resolves this via graph channel routing rather than geometric projection:

  • Topological Decoupling: The compiler treats the 3D twist purely as a boundary redirection in the connectivity graph (e.g., binding edge(p6, p7) to edge(p0, p1) in reverse order).
  • Planar Placement Algorithms: Spectral and force-directed graph algorithms lay out the topological nodes across the 2D Configurable Logic Block (CLB) array.
  • Synthesized Delay Equalization: When topological connections cross physical distances on the chip, the compiler automatically inserts target delay buffers along the graph channels to ensure synchronized signal propagation.

Through this unified model, physical geometry, manufacturable toolpaths, and silicon place-and-route are treated as equivalent graph reduction passes.

Drafting in progress…

Spatial Domains: Geometry Construction Inside IPL

1. Motivation

The Invocation Language describes causal relationships — completeness, fills, invocations — with no inherent notion of space. Some definitions, however, need to describe where things are: point coordinates, connectivity between points, and surfaces built from that connectivity. Rather than inventing a second language for this, MatterScript introduces the @domain directive so that a definition can bring a small set of geometry-construction functions into scope, and lets those functions build up a parallel, loosely linked geometry graph alongside the definition’s ordinary causal-network AST.

This matters beyond convenience. IPL’s whole premise is describing causal relationships in the abstract — Fant’s model doesn’t actually care whether the thing being resolved is a logic signal or a physical coordinate. The spatial domain is the first proof that the same language, with the same binding discipline and the same $-reference convention used everywhere else in IPL, can also describe physical structure. How digital networks and physical geometry eventually get coupled — a definition whose resolution depends on both a signal’s value and a point’s position, say — is still an open question, deliberately not addressed here. But the direction this points toward is a single MatterScript program describing both the electronics and the physical structure they live in, rather than two separate toolchains that have to be kept in sync by hand.

The two ASTs are kept deliberately separate for now:

  • The IPL network AST (network.Definition, network.Statement, …) is unchanged by any of this. No new grammar, no new statement kinds.
  • The spatial graph (spatial.SpatialGraph — points, edges, loops, faces) is built by a resolution pass that walks an already-parsed definition and interprets certain fills as geometry calls.

2. Scoping: @domain brings functions into scope

A definition declares its domain once, in its header:

Shape[
  ()
  ()
  @domain(spatial2d)
  ...
]

@domain(spatial2d | spatial3d) is existing IPL syntax (network.parseDomainSpec), previously used only to parametrize cellular-automata generate blocks. It now does double duty: when a definition’s domain_spec.kind is spatial2d or spatial3d, that definition’s resolution body is understood to have a set of domain-specific geometry functions in scope: point, edge, loop, and face.

There is no per-statement annotation to sprinkle through a definition’s body. The domain is declared once, at the top, exactly like any other @domain usage already in the language.

spatial1d is not a geometry domain — it remains scoped to its existing cellular-automata usage and has no function registry. Declaring @domain(spatial1d) and then calling point(...) inside it is treated as an ordinary (undefined-function) fill, not a geometry binding.

3. Binding: geometry calls are ordinary fills

No new syntax was introduced for defining a geometry entity. This is already-legal IPL:

p0<point(0.0, 0.0)>

This is parsed today as an ordinary Statement.fill:

dest_name: "p0"
expr:      "point(0.0, 0.0)"
parsed_expr: Expr{ kind: .call, func: "point", args: [...] }

The IPL parser (expressions.parseILExpr) already recognizes name(args...) inside <...> and produces a structured .call expression — this was true before any of the spatial-domain work began. The resolution pass (§6) is what gives that structure new meaning when the enclosing definition is domain-scoped to spatial2d/spatial3d and the call’s function name matches something in the registry; outside that context, the exact same syntax continues to behave exactly as it always has.

4. Reference: $name connects to an existing binding

To connect a new geometry call to something defined earlier in the same definition, use $name — the same sigil IPL already uses everywhere else to mean “look this up” rather than “declare this” (composed table keys, generate-block variables, ordinary fill expressions):

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>
e0<edge($p0, $p1)>

$p0 and $p1 parse as .variable expression nodes (distinct from the .constant nodes that plain numeric literals produce — see §7), and the resolution pass looks them up in the definition’s geometry-binding table built so far.

This applies uniformly to every kind of geometric element, not just points — edges are referenced by loop the same way points are referenced by edge, and loops are referenced by face the same way:

l0<loop($e0, $e1, $e2, $e3)>
f0<face($l0)>

There is no special inline/nested form (face(loop(edge(...), ...))) even though IPL’s expression grammar could parse one — every element, at every level, is bound once at the top level and referenced by $name from then on. This was a deliberate choice: it keeps every geometric element individually addressable, with a single uniform rule instead of a special case for elements built “inline.”

5. Binding semantics: define once, reference many times

A name may be bound to a geometry handle exactly once. After that first binding, it is immutable — later statements may reference it via $name as many times as needed, but nothing may redefine what it refers to.

Concretely:

  • The first p0<point(...)> encountered binds p0 to a new spatial.PointId in the resolution pass’s binding table.
  • Any later $p0 reference resolves to that same handle.
  • A second p0<point(...)> statement — an attempt to rebind an already-bound name — is rejected with error.DuplicateGeometryBinding, not silently overwritten and not silently ignored.

This mirrors ordinary single-assignment conventions and matches how a geometry graph is naturally authored: declare the entities that matter, then wire connectivity between them by reference, without ever having to worry that a later statement quietly changed what an earlier name meant.

6. Resolution: a single top-to-bottom pass

Geometry binding happens in a dedicated resolution pass (spatial_domain.resolveSpatialBindings), run over an already-parsed network.Definition — it does not run during parsing, and it does not alter the parser’s output. The pass:

  1. Confirms def.domain_spec is spatial2d or spatial3d (returns error.UnsupportedSpatialDomain for spatial1d, error.MissingDomainSpec if there’s no domain at all).
  2. Walks def.resolution in source order. For each .fill statement whose parsed_expr is a .call matching a name in the domain’s function registry (point, edge, loop, face), it dispatches to that function’s handler, which builds the corresponding entity in a spatial.SpatialGraph and returns a GeometryHandle.
  3. Records the handle against the fill’s dest_name in a std.StringHashMapUnmanaged(GeometryHandle) binding table — this table, together with the SpatialGraph, is the definition’s “geometry AST,” addressed by the same names the IPL source uses.
  4. Fill statements whose call name isn’t in the registry are left untouched, for ordinary fill-resolution logic elsewhere to handle.

Because this is a single forward pass, a name must be bound before it is referenced. Writing edge($p0, $p1) before either point is defined is a forward reference and is not currently supported —$p0 would resolve against an empty binding table and fail with error.UndefinedGeometryReference. This matches how geometry source is naturally written (points, then the things that connect them), so in practice it isn’t a real constraint on authoring — just documented here since it isn’t enforced by any grammar rule, only by pass order.

7. Argument shapes

Two argument shapes matter for the implemented functions:

  • Numeric literals (point(0.0, 0.0)’s arguments) arrive as Expr{ .kind = .constant, .name = "0.0" } — the raw text, not a pre-parsed number. The resolution pass parses this with std.fmt.parseFloat(f64, ...). This is a property of parseILCallArgument, not something specific to the spatial domain — worth knowing if you add a function whose arguments need different types.
  • Name references (edge($p0, $p1)’s arguments) arrive as Expr{ .kind = .variable, .name = "p0" }. Resolution looks the name up in the binding table and type-checks that the bound handle is the kind the function expects (e.g. edge requires two .point handles, loop requires .edge handles, face requires a single .loop handle).

8. Loops, closure, and a note on shared edges

loop($e0, $e1, ...) builds an ordered boundary from previously-bound edges, and validates that they actually close: each edge’s endpoint must match the next edge’s start point, and the last edge must return to the first edge’s starting point. A loop that doesn’t close raises error.DiscontinuousLoop or error.UnclosedLoop; fewer than 3 edges raises error.DegenerateLoop.

This closure check only cares about a single loop’s own continuity — it has no concept of two different loops needing to traverse a shared edge in opposite directions to stay consistent with each other (the way a proper watertight mesh normally works). In practice this means: when two faces share a physical edge, give each face its own edge binding for that segment, even though the two are geometrically identical. This is a small, deliberate duplication rather than a limitation to work around — it keeps every loop’s closure trivially self-contained and avoids needing any notion of “traverse this edge backward.”

9. The full pipeline: from IPL source to a mesh file

Putting the above together, an @domain(spatial2d|spatial3d) definition flows through four stages, each a separate, independently testable piece:

  .ms.ipl source
        |
        v
  ipl_parser.parse            (unchanged — ordinary IPL parsing)
        |
        v
  spatial_domain.resolveSpatialBindings   (§6 — builds SpatialGraph + binding table)
        |
        v
  spatial.triangulate         (SpatialGraph -> TriangulatedMesh)
        |
        v
  spatial.exportPly           (TriangulatedMesh -> binary PLY bytes)

This is wired into the CLI, not just tests: main.zig’s .ms.ipl handling now calls ipl_export_mesh.writeNetworkMeshes after parsing, which walks every definition in the file and, for each one whose domain_spec is spatial2d/spatial3d, runs it through the pipeline above and writes <definition-name>.ply under the run’s workspace directory (ipl_export_mesh.writeDefinitionMesh does the single- definition version of this, if you need to call it directly).

Definitions with no @domain, or with @domain(spatial1d), are left alone by the mesh exporter — they go through the existing VHDL emission path instead. A single .ms.ipl file can mix causal definitions and geometry definitions side by side; each is routed to the emitter that actually understands it, based purely on domain_spec. This is the seed of the unification mentioned in §1: one source file, one parser, two independent consumers of the same AST shape today — and eventually, ideally, definitions that draw on both at once.

10. Worked example: a unit square, start to finish

cube[()()
    @domain(spatial3d)

    // 8 corners: b0..b3 form the bottom face (z=0),
    // t0..t3 form the top face (z=1), directly above
    // their b-counterparts.
    p0< point(0.0, 0.0, 0.0) >  // b0
    p1< point(1.0, 0.0, 0.0) >  // b1
    p2< point(1.0, 1.0, 0.0) >  // b2
    p3< point(0.0, 1.0, 0.0) >  // b3
    p4< point(0.0, 0.0, 1.0) >  // t0
    p5< point(1.0, 0.0, 1.0) >  // t1
    p6< point(1.0, 1.0, 1.0) >  // t2
    p7< point(0.0, 1.0, 1.0) >  // t3

    // Each of the 6 faces owns its own 4 edges, even where an
    // edge geometrically coincides with one used by a neighboring
    // face (e.g. p0->p1 appears both here and in the front face).
    // This keeps every loop closing in a single consistent
    // direction with no reversed-edge traversal needed anywhere —
    // unlike the Möbius example, an ordinary cube's faces never
    // need to share a directed edge to stay correct.

    // bottom face: b0 -> b1 -> b2 -> b3 -> b0
    e_b0<edge($p0, $p1)>
    e_b1<edge($p1, $p2)>
    e_b2<edge($p2, $p3)>
    e_b3<edge($p3, $p0)>
    l_bottom<loop($e_b0, $e_b1, $e_b2, $e_b3)>
    f_bottom<face($l_bottom)>

    // top face: t0 -> t1 -> t2 -> t3 -> t0
    e_t0<edge($p4, $p5)>
    e_t1<edge($p5, $p6)>
    e_t2<edge($p6, $p7)>
    e_t3<edge($p7, $p4)>
    l_top<loop($e_t0, $e_t1, $e_t2, $e_t3)>
    f_top<face($l_top)>

    // front face: b0 -> b1 -> t1 -> t0 -> b0
    e_f0<edge($p0, $p1)>
    e_f1<edge($p1, $p5)>
    e_f2<edge($p5, $p4)>
    e_f3<edge($p4, $p0)>
    l_front<loop($e_f0, $e_f1, $e_f2, $e_f3)>
    f_front<face($l_front)>

    // right face: b1 -> b2 -> t2 -> t1 -> b1
    e_r0<edge($p1, $p2)>
    e_r1<edge($p2, $p6)>
    e_r2<edge($p6, $p5)>
    e_r3<edge($p5, $p1)>
    l_right<loop($e_r0, $e_r1, $e_r2, $e_r3)>
    f_right<face($l_right)>

    // back face: b2 -> b3 -> t3 -> t2 -> b2
    e_k0<edge($p2, $p3)>
    e_k1<edge($p3, $p7)>
    e_k2<edge($p7, $p6)>
    e_k3<edge($p6, $p2)>
    l_back<loop($e_k0, $e_k1, $e_k2, $e_k3)>
    f_back<face($l_back)>

    // left face: b3 -> b0 -> t0 -> t3 -> b3
    e_l0<edge($p3, $p0)>
    e_l1<edge($p0, $p4)>
    e_l2<edge($p4, $p7)>
    e_l3<edge($p7, $p3)>
    l_left<loop($e_l0, $e_l1, $e_l2, $e_l3)>
    f_left<face($l_left)>
    :
]

No sources/destinations are needed — this definition exists to produce geometry, not to compute a causal output, and neither the resolution pass nor the mesh exporter looks at def.destinations at all. (Note: if this definition were ever run through the VHDL emitter instead, an empty destination list would trigger Fant’s §12.3.4 single-return normalization, which assumes any zero-destination definition is implicitly returning something and would try to turn p0–f0 into VHDL ports. That normalization currently doesn’t distinguish a geometry-only definition from an ordinary one — the mesh exporter sidesteps it entirely by never invoking writeDefinition, but it’s a known rough edge if this definition path is ever exposed to VHDL emission by mistake.)

Walking it through the pipeline:

  1. Parse. Each pN<point(...)> and the rest parse as ordinary Statement.fill values, each carrying a structured .call parsed_expr — nothing spatial-domain-specific happens yet.
  2. Resolve. resolveSpatialBindings sees domain_spec.kind == .spatial2d, and walks the four point fills, then the four edge fills (each resolving its $pN arguments against the binding table built so far), then l0 (resolving its four $eN edge references and validating that they close: p0→p1→p2→p3→p0), then f0 (resolving its single $l0 reference). The result is a SpatialGraph with 4 points, 4 edges, 1 loop, 1 face, and a binding table mapping all nine names to their handles.
  3. Triangulate. spatial.triangulate turns the single quad face into 2 triangles over the same 4 vertices.
  4. Export. spatial.exportPly writes those 4 vertices and 2 triangular faces out as a binary PLY file — real bytes on disk, openable in MeshLab or any other mesh viewer.

11. Looking ahead

This is deliberately the smallest complete example: one face, closed by hand-traced edges, small enough to walk through statement by statement as above. The same primitives — point, edge, loop, face, the $-reference convention, define-once-reference-many binding — scale directly to more complex solids (multiple faces, each with its own edges per §8) without any new syntax or resolution logic.

The larger direction this is building toward, per §1, is a single MatterScript program in which the same language constructs — the same @domain scoping, the same binding discipline — describe both the digital logic of a system and the physical structure it’s embedded in or actuates. That coupling isn’t designed yet, and this document doesn’t attempt to anticipate its shape. What’s here is the first concrete proof that IPL’s causal-network model and physical geometry can be expressed side by side in the same file, resolved by independent passes over the same parsed AST, without the language itself needing to know in advance which one it’s describing.

Vertices

A spatial structure has to begin somewhere.

Before there can be an edge, there must be something for the edge to connect. Before there can be a loop, there must be edges to arrange. Before there can be a face, there must be a closed boundary.

In MatterScript, that foundation is the vertex.

A vertex is introduced with the point(...) geometry function:

p0<point(0.0, 0.0)>

At first glance, this looks like nothing more than a coordinate declaration. But the important part of the MatterScript model is that the coordinate is not left as anonymous numerical data. The statement gives the point a name, and that name becomes the way the rest of the geometry refers to it.

The progression is therefore:

coordinate
    ↓
point
    ↓
named binding
    ↓
PointId
    ↓
SpatialGraph vertex
    ↓
topological reference

The vertex is where numerical position becomes an addressable piece of geometry.


1. A Point Is a Named Geometric Entity

The simplest spatial definition can introduce a point directly:

Shape[
  ()
  ()

  @domain(spatial2d)

  p0<point(0.0, 0.0)>

]

The p0 on the left is not merely a variable in the conventional programming-language sense. It establishes a binding between the MatterScript name and a geometry entity.

During parsing, the statement is still an ordinary IPL fill:

destination: p0
expression:  point(0.0, 0.0)

The parser does not need to understand what a geometric point means.

The spatial resolution pass interprets the point(...) call because the enclosing definition has declared a spatial domain.

This separation is deliberate:

MatterScript source
       │
       ▼
    IPL parser
       │
       ▼
ordinary Statement.fill
       │
       ▼
spatial resolution
       │
       ▼
    SpatialGraph

Geometry therefore does not require a second language or a special geometry-specific grammar.

The same IPL expression machinery is reused.


2. The Spatial Domain Gives point Its Meaning

Geometry functions are brought into scope by the definition’s domain:

@domain(spatial2d)

or:

@domain(spatial3d)

Within those domains, the spatial resolver recognizes:

point
edge
loop
face

The important distinction is that the syntax itself does not change.

This:

p0<point(0.0, 0.0)>

is syntactically an ordinary fill regardless of where it appears.

Its interpretation depends on the surrounding domain.

That means the geometry system follows the same general principle as the rest of MatterScript:

Context gives structure meaning.

A point(...) call inside a spatial definition becomes a geometric binding. Outside that context, it remains an ordinary expression rather than being silently interpreted as geometry.


3. Coordinates Define Position

A point’s arguments are numerical coordinates.

In two dimensions:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>
p2<point(1.0, 1.0)>
p3<point(0.0, 1.0)>

These four points describe the corners of a unit square:

p3 ───────── p2
│             │
│             │
│             │
p0 ───────── p1

In three dimensions, the same construction simply receives a third coordinate:

p0<point(0.0, 0.0, 0.0)>
p1<point(1.0, 0.0, 0.0)>
p2<point(1.0, 1.0, 0.0)>
p3<point(0.0, 1.0, 0.0)>

p4<point(0.0, 0.0, 1.0)>
p5<point(1.0, 0.0, 1.0)>
p6<point(1.0, 1.0, 1.0)>
p7<point(0.0, 1.0, 1.0)>

The first four points form the lower layer of a cube. The second four form the corresponding upper layer.

The spatial resolver currently parses these numeric arguments as floating-point values.

Conceptually:

point(x, y)
point(x, y, z)

becomes a spatial point located at those coordinates.


4. Coordinates Are Not Identity

A useful distinction appears immediately when two geometric entities occupy the same location.

The coordinate describes position.

The MatterScript binding provides identity.

For example:

p0<point(0.0, 0.0)>

does two things:

  1. It specifies a location: (0.0, 0.0).
  2. It gives that geometric entity the name p0.

The name is what later geometry uses:

e0<edge($p0, $p1)>

The edge does not repeat the coordinates.

It does not say:

edge(0.0, 0.0, 1.0, 0.0)

Instead, it refers to previously established geometric entities:

$p0 ────────> first endpoint
$p1 ────────> second endpoint

This is an important part of the MatterScript geometry model.

Coordinates establish where. Names establish what. References establish relationships.


5. Define Once, Reference Many

Geometry bindings follow the same single-assignment discipline established elsewhere in IPL.

A name is bound once:

p0<point(0.0, 0.0)>

After that, the name can be referenced repeatedly:

e0<edge($p0, $p1)>
e3<edge($p3, $p0)>

Both references resolve to the same geometric point.

The binding is not replaced by a later statement.

A second definition such as:

p0<point(5.0, 5.0)>

is therefore not interpreted as “move p0.”

It is an attempt to bind an already-bound name again and is rejected as a duplicate geometry binding.

This distinction is important because the geometry graph is intended to remain structurally understandable after resolution.

Once:

p0 → PointId

has been established, every occurrence of:

$p0

means that same point.

There is no hidden reassignment.


6. The $ Reference Connects Geometry

The $ sigil is the bridge between independent geometry declarations.

Consider:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>

e0<edge($p0, $p1)>

The first two statements create geometric entities.

The third does not create new points.

Instead, it retrieves the existing bindings:

$p0 → p0 → PointId
$p1 → p1 → PointId

and uses those handles to construct an edge.

The same pattern continues upward through the geometry hierarchy:

p0<point(...)>
p1<point(...)>

e0<edge($p0, $p1)>

l0<loop($e0, ...)>

f0<face($l0)>

Each level references an entity that already exists.

POINT
  │
  │ $p0
  ▼
EDGE
  │
  │ $e0
  ▼
LOOP
  │
  │ $l0
  ▼
FACE

This gives the geometry graph a uniform binding discipline.


7. Resolution Is Ordered

Spatial bindings are resolved in a single top-to-bottom pass.

That means a point must exist before another geometric element can reference it.

This works:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>

e0<edge($p0, $p1)>

This does not:

e0<edge($p0, $p1)>

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>

At the time e0 is resolved, neither p0 nor p1 exists in the geometry binding table.

The resolver therefore reports an undefined geometry reference.

This gives spatial source code a natural construction order:

points
  ↓
edges
  ↓
loops
  ↓
faces

The source order mirrors the dependency structure of the geometry.


8. From Coordinates to PointId

Internally, the point does not remain merely a line of source code.

During spatial resolution, the point(...) handler creates a point in the SpatialGraph and returns a geometry handle representing that entity.

The binding table then associates the MatterScript name with that handle:

"p0"
  │
  ▼
PointId
  │
  ▼
SpatialGraph

Conceptually, the source:

p0<point(0.0, 0.0)>

becomes something like:

p0 ──────► PointId(0)
             │
             └──► (0.0, 0.0)

The exact internal identifier is an implementation detail.

The important abstraction is that MatterScript has transformed a textual name and a coordinate into an addressable entity within a geometric graph.

That graph is then available to subsequent spatial operations.


9. Vertices Are the Foundation of Topology

A point by itself has no connectivity.

It simply establishes a location.

An edge introduces the first topological relationship:

e0<edge($p0, $p1)>

Now two vertices have become connected.

Multiple edges can then form a boundary:

l0<loop($e0, $e1, $e2, $e3)>

And the closed boundary can become a face:

f0<face($l0)>

The progression is therefore:

                    GEOMETRY

                 ┌───────────┐
                 │   FACE    │
                 └─────▲─────┘
                       │
                     loop
                       │
                 ┌─────┴─────┐
                 │    LOOP   │
                 └─────▲─────┘
                       │
                     edges
                       │
                 ┌─────┴─────┐
                 │   EDGES   │
                 └─────▲─────┘
                       │
                    points
                       │
                 ┌─────┴─────┐
                 │  VERTICES │
                 └───────────┘

The vertex is therefore not merely the smallest drawable object.

It is the reference point from which topology is constructed.


10. A Vertex Can Participate in Many Relationships

Because bindings are immutable and references can be reused, a single point can participate in many geometric structures.

Consider the corner of a cube:

p0<point(0.0, 0.0, 0.0)>

That same vertex can be referenced by several edges:

e_b0<edge($p0, $p1)>
e_f3<edge($p4, $p0)>
e_l1<edge($p0, $p4)>

The point is not copied each time.

Each $p0 resolves to the same underlying point handle.

This creates a distinction between geometric identity and topological participation:

                     p0
                  PointId
                     │
          ┌──────────┼──────────┐
          │          │          │
          ▼          ▼          ▼
        edge       edge       edge
          │          │          │
          ▼          ▼          ▼
        face       face       face

The vertex can therefore become a shared anchor for multiple structures without changing its own definition.


11. The Vertex List Survives Triangulation

The spatial graph is not necessarily the final representation consumed by downstream tools.

After spatial resolution, a geometry graph can be triangulated:

SpatialGraph
     │
     ▼
triangulate
     │
     ▼
TriangulatedMesh

For example, a quadrilateral face can become two triangles while continuing to use the same four vertices.

Before:

p3 ───────── p2
│             │
│    FACE     │
│             │
p0 ───────── p1


After triangulation:

p3 ───────── p2
│ \           │
│   \         │
│     \       │
p0 ───────── p1

The topology changes at the face representation level, but the underlying vertex positions remain the same.

This distinction becomes important when the geometry is exported.

A four-vertex quad can become:

Vertices:
    p0
    p1
    p2
    p3

Triangles:
    (p0, p1, p2)
    (p0, p2, p3)

The vertices are therefore fundamental data, not merely temporary construction syntax.


12. Vertices and the Mesh Boundary

Eventually the geometry can cross the runtime boundary into a conventional mesh format.

The current pipeline is:

.ms.ipl source
      │
      ▼
 IPL parser
      │
      ▼
SpatialGraph
      │
      ▼
TriangulatedMesh
      │
      ▼
 binary PLY
      │
      ▼
 MeshLab / mesh tools

A simple spatial definition containing four points, four edges, one loop, and one face can therefore become a mesh containing four vertices and two triangular faces.

The important thing is that the original MatterScript source remains the description from which that structure was constructed.

The PLY file is an output representation.

The MatterScript source describes the geometry at the level of named entities and relationships.


13. Geometry Begins with Addressable Position

The significance of the vertex becomes clearer when viewed from the entire spatial model.

MatterScript does not begin with a mesh.

It begins with named geometric facts:

p0<point(0.0, 0.0, 0.0)>
p1<point(1.0, 0.0, 0.0)>
p2<point(1.0, 1.0, 0.0)>
p3<point(0.0, 1.0, 0.0)>

Those facts are then connected:

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

The connections are organized:

l0<loop($e0, $e1, $e2, $e3)>

And the boundary is promoted into a surface:

f0<face($l0)>

Nothing in that sequence requires an external geometry editor.

The geometry emerges from the same fundamental mechanism used elsewhere in IPL:

name
  +
expression
  +
reference
  =
structure

14. Why Vertices Matter to MatterScript

The vertex is the first place where MatterScript’s abstract binding model acquires physical position.

An ordinary IPL relationship might establish that one thing is connected to another.

A spatial relationship can establish something more:

this entity
     │
     └── exists at this position

Once positions become named and addressable, those names can participate in larger structures.

That makes the spatial graph compatible with the broader MatterScript philosophy:

Geometry is not an artifact produced after computation. Geometry can itself be described as computation.

The current implementation keeps the causal-network AST and spatial graph as separate structures. That separation is intentional. But the spatial domain demonstrates that the same language can already describe both kinds of structure using the same fundamental ideas:

  • definitions,
  • fills,
  • names,
  • references,
  • domains,
  • resolution,
  • immutable bindings.

The vertex is the simplest concrete example of that unification.


15. Complete Example

A minimal spatial definition containing four vertices can be written as:

Square[
  ()
  ()

  @domain(spatial2d)

  p0<point(0.0, 0.0)>
  p1<point(1.0, 0.0)>
  p2<point(1.0, 1.0)>
  p3<point(0.0, 1.0)>
]

At this stage, there is no topology yet.

There are simply four named geometric entities:

p3 ───────── p2
│             │
│             │
│             │
p0 ───────── p1

Adding edges establishes connectivity:

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

The edges can then form a loop:

l0<loop($e0, $e1, $e2, $e3)>

And the loop can become a face:

f0<face($l0)>

The complete progression is:

  p0  p1  p2  p3
   │   │   │   │
   └───┴───┴───┘
       edges
         │
         ▼
        loop
         │
         ▼
        face
         │
         ▼
   triangulated mesh
         │
         ▼
       PLY file

The vertex is where this entire chain begins.


16. Looking Ahead: Edges

A vertex establishes position.

An edge establishes connection.

That is the next step in the spatial construction model.

Once two named vertices exist:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>

MatterScript can express their relationship directly:

e0<edge($p0, $p1)>

The edge does not need to know the coordinates independently.

It only needs to know which vertices it connects.

This is the fundamental transition from geometry as position to geometry as topology.

The next chapter examines that transition in detail.

Drafting in progress…

Edges - Connectivity as Geometry

A vertex gives geometry a position.

An edge gives two positions a relationship.

In MatterScript, an edge is not introduced as an independent line segment with its own coordinates. It is constructed by referencing two previously defined vertices:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>

e0<edge($p0, $p1)>

The important idea is that the edge does not duplicate the geometry of its endpoints. It connects existing geometric entities by name.

This is the next step in the progression established by vertices:

        point
          │
          ▼
     named position
          │
          ▼
        edge
          │
          ▼
   named connectivity

MatterScript therefore builds geometry incrementally. Points establish where. Edges establish between.


1. An Edge Connects Existing Vertices

The edge function takes two geometry references:

e0<edge($p0, $p1)>

Here:

  • p0 is an existing point binding.
  • p1 is an existing point binding.
  • $p0 and $p1 refer to those bindings.
  • e0 becomes the name of the resulting edge.

The edge does not contain a second copy of the coordinates.

Instead, its structure can be understood conceptually as:

p0 ─────────────────── p1
        │
        │
       e0

The geometry graph now contains three named entities:

p0 ──────────────── p1
        e0

The two vertices provide the positions. The edge provides the relationship between them.

This is a recurring MatterScript principle: relationships are constructed through references rather than by duplicating the things being related.


2. edge(...) Is a Domain Function

Edges use the same mechanism introduced by the spatial-domain system.

A definition first establishes its spatial domain:

Shape[
    ()
    ()
    @domain(spatial2d)

    ...
]

Within a spatial2d or spatial3d definition, the geometry functions

point(...)
edge(...)
loop(...)
face(...)

are brought into scope.

There is no separate geometry grammar for edges.

The following is simply an IPL fill whose expression happens to be a call:

e0<edge($p0, $p1)>

The parser already understands the general form:

name(arguments...)

as a structured call expression. The spatial resolution pass is what gives edge(...) its geometric meaning when the enclosing definition has a supported spatial domain.

This separation is important.

The parser does not need to know that edge represents geometry. The geometry resolver interprets the already-parsed expression and constructs the corresponding entity in the SpatialGraph.


3. $ Means Reference

The arguments to edge(...) are not new points.

They are references to points that have already been bound.

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>

e0<edge($p0, $p1)>

The $ sigil tells MatterScript to look up an existing binding.

This is the same reference convention used elsewhere in IPL. Geometry does not introduce a second mechanism for connecting things.

The progression is therefore uniform:

p0<point(...)>
p1<point(...)>

e0<edge($p0, $p1)>

followed by:

l0<loop($e0, $e1, $e2, $e3)>

and eventually:

f0<face($l0)>

Every geometric level uses the same rule:

Define an entity once. Reference it by name thereafter.


4. An Edge Requires Points

The geometry resolver does not merely look up names. It also checks that the referenced geometry has the expected kind.

An edge requires two .point handles.

Conceptually:

edge(
    Point,
    Point
)

So this is valid:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>
e0<edge($p0, $p1)>

while an edge cannot be constructed by passing an unrelated geometry handle where a point is expected.

The same type relationship will appear again as geometry grows:

point ──► edge ──► loop ──► face

Each level consumes the geometry constructed by the level below it.

This gives the spatial graph a simple structural hierarchy without requiring a separate type declaration for every geometric object.


5. Binding Happens in Source Order

Spatial geometry is resolved in a single forward pass.

The resolver walks the definition in source order, constructs each recognized geometry element, and records its resulting handle in the geometry binding table.

That means a point must exist before an edge can reference it.

This is valid:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>

e0<edge($p0, $p1)>

This is not currently valid:

e0<edge($p0, $p1)>

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>

The second form attempts to use forward references. At the time e0 is resolved, neither point has been entered into the geometry binding table.

The result is:

error.UndefinedGeometryReference

This is not a special syntactic restriction on edge. It follows directly from the implementation of the spatial resolution pass.

The natural authoring order is therefore also the resolution order:

points
  ↓
edges
  ↓
loops
  ↓
faces

6. Define Once, Reference Many

An edge binding follows the same single-assignment rule established for vertices.

Once:

e0<edge($p0, $p1)>

has bound the name e0, subsequent geometry may reference it:

l0<loop($e0, $e1, $e2, $e3)>

The meaning of e0 does not change.

A second attempt to bind the same name is rejected as a duplicate geometry binding rather than silently replacing the original entity.

This gives the geometry graph a useful property:

        e0
       /  \
     p0    p1

e0 always means this edge.

The name becomes an address for the geometric entity.

This is particularly important once a model contains many edges. Geometry remains understandable because connectivity is expressed through stable names rather than through repeated coordinate data.


7. Edge Direction Is Part of Its Construction

An edge is written with an ordered pair:

e0<edge($p0, $p1)>

The source establishes the edge from p0 to p1.

That ordering becomes important when edges are assembled into loops.

For example:

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

These edges form the sequence:

p0 → p1 → p2 → p3 → p0

The edges are not merely four unrelated segments. Their endpoint relationships provide the information from which a closed boundary can be recognized.

That is why the next geometric primitive, loop(...), takes ordered edge references rather than simply receiving a collection of points.

The edge is where connectivity begins to acquire topology.


8. Building a Boundary from Edges

Consider a square:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>
p2<point(1.0, 1.0)>
p3<point(0.0, 1.0)>

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

The resulting structure can be visualized as:

p3 ─────────────── p2
│                   │
│                   │
│                   │
p0 ─────────────── p1

e3                  e1
        e0     e2

More precisely, the directed sequence is:

p0 ──e0──► p1
             │
             e1
             ▼
             p2
             │
             e2
             ▼
             p3
             │
             e3
             ▼
             p0

Nothing new had to be invented to describe this boundary.

The points already existed.

The edges simply expressed how those points are connected.

This is one of the central advantages of constructing geometry through references: topology emerges from the relationships between named entities.


9. Edges Belong to the Spatial Graph

During resolution, each recognized geometry call produces an entity in the spatial.SpatialGraph.

The geometry binding table associates the source-level name with the resulting geometry handle.

Conceptually:

MatterScript source
        │
        ▼
e0<edge($p0, $p1)>
        │
        ▼
Geometry resolver
        │
        ├── lookup p0
        ├── lookup p1
        │
        ▼
   SpatialGraph
        │
        ▼
     EdgeId

The important separation is that the source name and the graph’s internal representation serve different purposes.

The MatterScript author works with:

$p0
$p1
$e0

while the spatial implementation can work with its internal geometry handles.

The binding table connects those two worlds.

This is the same pattern established for vertices: source-level names provide stable addresses into the constructed geometry graph.


10. Coincident Edges and Shared Geometry

There is an important detail in the current implementation when multiple faces meet.

Two faces may contain an edge occupying exactly the same geometric segment. For example, both may contain an edge corresponding to:

p0 → p1

The current spatial model does not require those two faces to share one edge binding.

Instead, each face can have its own edge binding for the geometrically identical segment.

For example:

e_front<edge($p0, $p1)>
...
e_back<edge($p0, $p1)>

These are distinct bindings even though the underlying endpoints describe the same geometric segment.

This is deliberate in the current implementation.

A loop validates its own continuity by examining the ordered edges supplied to it. It does not currently reason about neighboring loops traversing a shared edge in the opposite direction.

Giving each face its own edge binding therefore keeps each loop’s boundary self-contained and avoids requiring a separate reversed-edge traversal mechanism.

The distinction is worth remembering:

same coordinates
      ≠
same binding

Two edges can describe geometrically coincident segments while still being separately named entities in the source.


11. Why Not Just Connect Coordinates Directly?

It would be possible to imagine a geometry syntax in which a loop simply contained raw coordinates:

loop(
    (0,0),
    (1,0),
    (1,1),
    (0,1)
)

That is not how MatterScript’s spatial construction works.

Instead:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>
p2<point(1.0, 1.0)>
p3<point(0.0, 1.0)>

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

Each level remains independently addressable.

The advantage is not merely syntactic.

The graph now knows that:

p0

is an entity that can participate in multiple relationships.

Likewise:

e0

is an entity that can participate in a loop.

The geometry is therefore constructed from named relationships, not from repeatedly embedded coordinate descriptions.


12. Edges Scale Naturally

The same mechanism used for one line segment scales to complete structures.

A four-edge boundary:

p0 → p1 → p2 → p3 → p0

can become a loop.

Multiple loops can become multiple faces.

Multiple faces can describe a three-dimensional structure.

The syntax does not change as the model becomes larger:

point(...)
edge(...)
loop(...)
face(...)

Nor does the reference mechanism change:

$p0
$p1
$e0
$l0

The graph simply becomes larger.

This is the same principle that makes the spatial domain useful as a language feature rather than merely as a geometry-export convenience: the complexity of the structure grows without requiring a different representation for every scale.


13. From Edges to Loops

An edge is the first geometric primitive that describes connectivity.

But connectivity alone does not yet define a surface.

Four edges such as:

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

still need to be interpreted as an ordered, closed boundary.

That is the job of loop(...):

l0<loop($e0, $e1, $e2, $e3)>

The loop resolver verifies that the edges form a continuous path and that the path closes back on itself.

The progression is now explicit:

POINT
  │
  │ position
  ▼
EDGE
  │
  │ connectivity
  ▼
LOOP
  │
  │ boundary
  ▼
FACE
  │
  │ surface
  ▼
MESH

The edge is therefore the bridge between position and topology.


14. The Geometry Graph Is Built Incrementally

A complete spatial definition can be read almost like a construction sequence:

@domain(spatial3d)

// 1. Positions
p0<point(0.0, 0.0, 0.0)>
p1<point(1.0, 0.0, 0.0)>
p2<point(1.0, 1.0, 0.0)>
p3<point(0.0, 1.0, 0.0)>

// 2. Connectivity
e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

// 3. Boundary
l0<loop($e0, $e1, $e2, $e3)>

// 4. Surface
f0<face($l0)>

Each statement adds another layer of structure.

Nothing at the edge level needs to know how a face will eventually use it.

Nothing at the point level needs to know that it will eventually participate in a face.

The graph is assembled through references, one relationship at a time.

This is the same loosely linked geometry graph introduced by the spatial-domain architecture: geometry is constructed alongside the ordinary IPL network without changing the underlying parser or requiring a second language.


15. Edges and the Mesh Pipeline

Once the geometry has been resolved, the edge itself does not need to be exported directly as a mesh primitive.

Instead, edges contribute to the higher-level topology from which a mesh can be generated.

The pipeline is:

MatterScript
     │
     ▼
IPL Parser
     │
     ▼
Spatial Binding Resolution
     │
     ├── Points
     ├── Edges
     ├── Loops
     └── Faces
     │
     ▼
Triangulation
     │
     ▼
TriangulatedMesh
     │
     ▼
PLY

For example, a single quadrilateral face can be constructed from four points, four edges, one loop, and one face. Triangulation then turns that face into two triangles over the same four vertices.

The edge therefore occupies an important middle layer:

coordinates
     ↓
vertices
     ↓
edges
     ↓
topological boundary
     ↓
surface
     ↓
triangles

It is the structure that makes the transition from isolated positions to connected geometry possible.


16. A Complete Edge Example

Here is the complete construction of a simple square:

Square[()()
    @domain(spatial2d)

    p0<point(0.0, 0.0)>
    p1<point(1.0, 0.0)>
    p2<point(1.0, 1.0)>
    p3<point(0.0, 1.0)>

    e0<edge($p0, $p1)>
    e1<edge($p1, $p2)>
    e2<edge($p2, $p3)>
    e3<edge($p3, $p0)>

    l0<loop($e0, $e1, $e2, $e3)>

    f0<face($l0)>
]

The construction reads from the bottom of the geometric hierarchy upward:

p0 ─────────────── p1
│                   │
│                   │
│                   │
p3 ─────────────── p2

  Points
     ↓
  Edges
     ↓
  Loop
     ↓
  Face

The important part is not the square itself. The important part is that every stage uses the same language mechanisms:

  • @domain establishes the spatial context.
  • point(...) creates named positions.
  • edge(...) connects named positions.
  • $name references existing entities.
  • loop(...) consumes named edges.
  • face(...) consumes a named loop.

No separate geometry language is necessary.


17. Connectivity Becomes Structure

The vertex chapter established a fundamental distinction:

A coordinate is not yet an entity until it has an identity in the geometry graph.

Edges establish the next distinction:

Two entities are not yet connected merely because they occupy related coordinates. The relationship itself must be represented.

That representation is the edge.

MatterScript therefore treats connectivity as something that can be named, referenced, validated, and composed.

The result is a progression from numerical data toward structure:

coordinate
    ↓
named point
    ↓
named edge
    ↓
ordered loop
    ↓
face
    ↓
mesh

Each level adds information without discarding the level below it.

This is what makes the spatial graph useful as a computational representation rather than merely as an intermediate format for drawing geometry.


18. Looking Ahead: Loops

An edge establishes a connection between two vertices.

A collection of appropriately ordered edges can establish something more important: a closed boundary.

That is the role of loop(...).

l0<loop($e0, $e1, $e2, $e3)>

The loop takes the connectivity already established by the edges and verifies that it forms a continuous, closed structure.

The next step in MatterScript’s geometric hierarchy is therefore:

Edges establish connectivity. Loops establish boundaries.

Once a boundary exists, it can become a surface through face(...).

The geometry hierarchy is now complete enough to see the larger pattern:

              FACE
               ▲
               │
             LOOP
               ▲
               │
              EDGE
               ▲
               │
             POINT

Geometry is being constructed not by embedding ever-larger descriptions inside one another, but by naming each level and composing those names into the next level of structure.

Drafting in progress…

Loops: Boundaries as Ordered Structure

An edge connects two vertices.

A loop turns a sequence of connected edges into a boundary.

In MatterScript, a loop is constructed by referencing previously defined edges:

l0<loop($e0, $e1, $e2, $e3)>

The important detail is that a loop is not simply a collection of edges. The edges are supplied in order, and that order defines how the boundary is traversed.

For example:

p0 ──e0──► p1
             │
             e1
             ▼
             p2
             │
             e2
             ▼
             p3
             │
             e3
             ▼
             p0

The loop represents the closed path:

p0 → p1 → p2 → p3 → p0

This establishes the next level of MatterScript’s geometric hierarchy:

POINT
   │
   │ position
   ▼
EDGE
   │
   │ connectivity
   ▼
LOOP
   │
   │ boundary
   ▼
FACE
   │
   │ surface
   ▼
MESH

A vertex establishes where.

An edge establishes between.

A loop establishes around.


1. A Loop Is an Ordered Boundary

The simplest loop consists of three or more edges whose endpoints form a continuous closed path.

A square can be described as:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>
p2<point(1.0, 1.0)>
p3<point(0.0, 1.0)>

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

l0<loop($e0, $e1, $e2, $e3)>

The four edges are not passed to loop(...) in arbitrary order.

They describe a traversal:

e0 → e1 → e2 → e3

which corresponds to:

p0 → p1 → p2 → p3 → p0

The loop therefore carries more information than the existence of four edges.

It carries their ordered connectivity.


2. Loops Use the Same Binding Model

No new reference mechanism is needed for loops.

The edges are already named:

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

The loop simply references them:

l0<loop($e0, $e1, $e2, $e3)>

This follows the same define-once, reference-many discipline used by vertices and edges.

point
  │
  └── named binding

edge
  │
  └── named binding

loop
  │
  └── named binding

Each level can therefore be addressed independently.

The $ sigil always means the same thing:

Look up an existing binding.

There is no special syntax for geometry composition.


3. A Loop Consumes Edges

The type relationship between the primitives is straightforward:

point(...)
    ↓
edge(point, point)
    ↓
loop(edge, edge, edge, ...)
    ↓
face(loop)

An edge requires two point handles.

A loop requires edge handles.

A face will require a loop handle.

This creates a progressively richer structure without requiring the higher-level primitive to duplicate the lower-level geometry.

For example:

e0<edge($p0, $p1)>

does not copy p0 or p1.

Likewise:

l0<loop($e0, $e1, $e2, $e3)>

does not copy the edges.

The loop is a new geometric entity whose structure is defined by its references to existing edges.


4. Closure Is the Essential Property

The defining characteristic of a loop is closure.

For:

l0<loop($e0, $e1, $e2, $e3)>

the resolver checks that each edge connects correctly to the next.

If:

e0 = p0 → p1
e1 = p1 → p2
e2 = p2 → p3
e3 = p3 → p0

then the boundary is continuous and closed:

p0 → p1 → p2 → p3 → p0

The final edge returns to the starting point of the first edge.

That final connection is what turns a path into a loop.

Conceptually:

OPEN PATH

p0 → p1 → p2 → p3


CLOSED PATH

p0 → p1 → p2 → p3
↑               │
└───────────────┘

A loop is therefore not simply “some edges.”

It is an edge sequence that successfully returns to where it began.


5. Continuity Between Edges

For a loop to be continuous, the endpoint of each edge must match the starting point of the next.

Consider:

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>

These edges connect correctly:

p0 → p1 → p2

The endpoint of e0 is p1.

The starting point of e1 is also p1.

Now consider:

e0<edge($p0, $p1)>
e1<edge($p2, $p3)>

These do not form a continuous path:

p0 → p1

p2 → p3

There is no connection between p1 and p2.

When the loop resolver encounters such a sequence, it reports:

error.DiscontinuousLoop

The loop primitive therefore performs an important piece of structural validation.

It does not merely record the author’s intention.

It verifies that the referenced edges actually form the boundary being claimed.


6. The Final Edge Must Close the Loop

Continuity between adjacent edges is not enough.

The final edge must also return to the first edge’s starting point.

Consider:

e0 = p0 → p1
e1 = p1 → p2
e2 = p2 → p3
e3 = p3 → p4

This is continuous:

p0 → p1 → p2 → p3 → p4

But it is not closed.

The path ends at p4.

It began at p0.

If the final edge does not return to p0, the resolver reports:

error.UnclosedLoop

The distinction is useful:

continuous ≠ closed

A continuous path can remain open.

A loop must be both continuous and closed.


7. A Loop Needs at Least Three Edges

The current implementation also rejects loops containing fewer than three edges.

For example:

l0<loop($e0, $e1)>

is not a valid loop.

It produces:

error.DegenerateLoop

The minimum valid structure is therefore three edges, such as a triangle:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>
p2<point(0.5, 1.0)>

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p0)>

l0<loop($e0, $e1, $e2)>

The resulting traversal is:

p0 → p1 → p2 → p0

This is the smallest ordinary closed polygonal boundary represented by the current loop primitive.


8. Source Order Becomes Geometric Order

MatterScript geometry is resolved in source order.

That means the normal construction sequence is:

points
  ↓
edges
  ↓
loops
  ↓
faces

For a loop, the referenced edges must already exist:

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

l0<loop($e0, $e1, $e2, $e3)>

The resolver walks the already-parsed definition from top to bottom.

When it reaches l0, the four edge bindings are already available in the geometry binding table.

This is the same forward-resolution rule used by the lower-level geometry primitives.

A loop cannot currently refer to an edge that has not yet been bound.


9. The Order of Edges Matters

Consider these edges:

e0 = p0 → p1
e1 = p1 → p2
e2 = p2 → p3
e3 = p3 → p0

The natural loop is:

l0<loop($e0, $e1, $e2, $e3)>

because the edges form:

p0 → p1 → p2 → p3 → p0

The loop therefore contains an ordered boundary.

This is different from treating the edges as an unordered set.

The same four edges contain enough information to describe the square, but the loop declaration tells the resolver how those edges are intended to be traversed.

This ordering is what allows closure to be checked locally.


10. Loops Turn Connectivity into Topology

An edge tells us that two points are connected.

A loop tells us that several such connections form a boundary.

That is an important conceptual transition.

POINT

"I exist here."


EDGE

"I connect these two positions."


LOOP

"I traverse these connections and return to where I started."

The geometry is becoming topological structure.

The coordinates are still present, but they are no longer the whole description.

The relationship between entities has become part of the model.

MatterScript can therefore move from numerical geometry toward structural geometry using the same mechanism throughout:

coordinates
    ↓
named entities
    ↓
named relationships
    ↓
ordered boundaries

11. A Loop Does Not Recreate Its Edges

The loop:

l0<loop($e0, $e1, $e2, $e3)>

does not contain four new line segments.

It refers to four existing edge bindings.

This distinction matters because the same underlying points can participate in many different geometric relationships.

For example, a point can belong to multiple edges:

        e1
       /
      p1
     /  \
   e0    e2
   /      \
 p0        p2

Likewise, a geometry definition can contain many loops built from different edge sequences.

The graph remains compositional because each entity is defined once and referenced thereafter.


12. Shared Edges: An Important Current Design Choice

When two faces meet, their boundaries can contain geometrically coincident edges.

A conventional watertight mesh often treats such a segment as a shared edge traversed in opposite directions by neighboring faces.

The current MatterScript spatial implementation takes a simpler approach.

Each face should have its own edge binding for a shared physical segment.

For example:

e_front<edge($p0, $p1)>

and elsewhere:

e_back<edge($p0, $p1)>

These edges are geometrically coincident, but they are separate source-level bindings.

This is deliberate.

The current loop closure logic validates one loop at a time. It does not reason about two neighboring loops needing to traverse a shared edge in opposite directions.

Giving each loop its own edge binding keeps the closure problem local:

Face A
  └── e_a0
      e_a1
      e_a2
      e_a3
          ↓
        loop A


Face B
  └── e_b0
      e_b1
      e_b2
      e_b3
          ↓
        loop B

This avoids introducing a separate concept of “reverse this edge” into the current geometry model.

It is a small amount of deliberate duplication in exchange for simple, independently verifiable loops.


13. Loops Are Independent Geometry Entities

Once resolved, the loop receives its own geometry handle and is recorded in the geometry binding table under its source name.

Conceptually:

MatterScript

l0<loop($e0, $e1, $e2, $e3)>
          │
          ▼
   geometry resolver
          │
          ▼
     SpatialGraph
          │
          ▼
       LoopId

The source-level name remains important:

$l0

can subsequently be used by a face:

f0<face($l0)>

The face does not need to reconstruct the boundary.

It simply references the already validated loop.

This gives the geometry hierarchy a clean separation of responsibilities:

edge
  └── establishes connectivity

loop
  └── validates and names the boundary

face
  └── gives that boundary surface meaning

14. The Loop Is the Boundary Between Geometry and Surface

Before a loop exists, we have connected line segments.

After a loop exists, we have a closed boundary that can enclose a surface.

This is why loop(...) occupies such an important place in the geometry hierarchy.

Points
  ↓
positions


Edges
  ↓
connections


Loops
  ↓
boundaries


Faces
  ↓
surfaces

The loop is therefore the structural bridge between wire-like connectivity and surface geometry.

A face can now be defined simply:

f0<face($l0)>

The face does not need the points again.

It does not need the edges again.

The loop already contains the ordered boundary relationship.


15. From Loop to Face

Consider the complete sequence:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>
p2<point(1.0, 1.0)>
p3<point(0.0, 1.0)>

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

l0<loop($e0, $e1, $e2, $e3)>

f0<face($l0)>

The construction can be read as a sequence of increasingly meaningful statements:

 p0, p1, p2, p3
        │
        ▼
   four positions
        │
        ▼
 e0, e1, e2, e3
        │
        ▼
 four connections
        │
        ▼
        l0
        │
        ▼
 closed boundary
        │
        ▼
        f0
        │
        ▼
      surface

Each layer uses the result of the previous layer.

That is the central construction pattern of MatterScript spatial geometry.


16. Loops in Three Dimensions

The loop primitive is not limited to flat two-dimensional examples.

The same construction applies to boundaries of faces in three-dimensional geometry.

For example, a cube face can be described by four three-dimensional points:

p0<point(0.0, 0.0, 0.0)>
p1<point(1.0, 0.0, 0.0)>
p2<point(1.0, 1.0, 0.0)>
p3<point(0.0, 1.0, 0.0)>

followed by:

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

and then:

l0<loop($e0, $e1, $e2, $e3)>

The loop itself still represents the same kind of object: an ordered, closed boundary.

The coordinates happen to live in three-dimensional space.

This is one reason the same primitive vocabulary can serve both spatial2d and spatial3d.


17. The Complete Geometry Pipeline

At this point, the construction pipeline is becoming explicit:

.ms.ipl source
      │
      ▼
 IPL parser
      │
      ▼
Spatial binding resolution
      │
      ├── points
      ├── edges
      ├── loops
      └── faces
      │
      ▼
SpatialGraph
      │
      ▼
Triangulation
      │
      ▼
TriangulatedMesh
      │
      ▼
PLY

The loop is resolved before triangulation.

That matters because triangulation operates on the higher-level geometry structure rather than asking the mesh exporter to infer boundaries from an unstructured collection of segments.

The boundary is established explicitly in the MatterScript source.


18. A Complete Loop Example

Here is a complete triangular boundary:

Triangle[()()
    @domain(spatial2d)

    p0<point(0.0, 0.0)>
    p1<point(1.0, 0.0)>
    p2<point(0.5, 1.0)>

    e0<edge($p0, $p1)>
    e1<edge($p1, $p2)>
    e2<edge($p2, $p0)>

    l0<loop($e0, $e1, $e2)>

    f0<face($l0)>
]

The structure is:

          p2
         /  \
       e1    e2
       /      \
     p1──e0──►p0

with traversal:

p0 → p1 → p2 → p0

The three edges form a continuous path.

The final edge returns to the starting point.

The loop is therefore valid.

The face can then consume the loop.


19. Why Loops Matter to MatterScript

A generic geometry library could treat a polygon as simply a list of coordinates.

MatterScript instead exposes the intermediate structures explicitly:

Point
Edge
Loop
Face

That choice makes the construction process visible.

The programmer can see:

  • where positions are established,
  • where connectivity is introduced,
  • where closure is validated,
  • where a boundary becomes a surface.

The geometry is therefore not just an output format.

It is represented as a hierarchy of named, composable relationships.

This is particularly important to MatterScript’s larger goal of bringing physical structure into the same language used to describe computation.

A loop is not merely something needed to draw a polygon.

It is an explicit statement about boundary structure.


20. From Boundaries to Surfaces

The geometric progression is now complete enough to state directly:

Vertices define position.

Edges define connectivity.

Loops define boundaries.

Faces define surfaces.

The loop is the point at which connectivity becomes a meaningful enclosed structure.

That structure can then be consumed by face(...):

f0<face($l0)>

and ultimately participate in triangulation and mesh generation.

The hierarchy is:

          FACE
            ▲
            │
          LOOP
            ▲
            │
           EDGE
            ▲
            │
          POINT

Each layer adds structure without abandoning the entities below it.


21. Looking Ahead: Faces

A loop establishes a closed boundary.

The next question is what that boundary encloses.

That is the role of a face.

In MatterScript:

f0<face($l0)>

takes the named loop and promotes its boundary into a surface-level geometric entity.

The construction sequence has therefore reached the next conceptual boundary:

POINT
  │
  │ position
  ▼
EDGE
  │
  │ connectivity
  ▼
LOOP
  │
  │ boundary
  ▼
FACE
  │
  │ surface
  ▼
MESH

A point tells us where.

An edge tells us between.

A loop tells us around.

A face will tell us across.

Drafting in progress…

Faces: Surfaces as Geometry

A loop establishes a boundary.

A face gives that boundary a surface.

In MatterScript, a face is constructed by referencing a previously defined loop:

f0<face($l0)>

The important idea is that a face does not redefine its edges, points, or coordinates.

It references an existing boundary and promotes that boundary into a geometric region.

This completes the core hierarchy of MatterScript spatial geometry:

POINT
   │
   │ position
   ▼
EDGE
   │
   │ connectivity
   ▼
LOOP
   │
   │ boundary
   ▼
FACE
   │
   │ surface
   ▼
MESH

A vertex establishes where.

An edge establishes between.

A loop establishes around.

A face establishes across.


1. A Face Is a Surface

Consider a square boundary:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>
p2<point(1.0, 1.0)>
p3<point(0.0, 1.0)>

e0<edge($p0, $p1)>
e1<edge($p1, $p2)>
e2<edge($p2, $p3)>
e3<edge($p3, $p0)>

l0<loop($e0, $e1, $e2, $e3)>

The loop establishes a closed boundary:

p0 → p1 → p2 → p3 → p0

But a boundary is not yet a surface.

The face provides that final step:

f0<face($l0)>

Conceptually:

Before:

p0 ───────── p1
│            │
│            │
│            │
p3 ───────── p2

Boundary only


After:

┌───────────────┐
│               │
│     FACE      │
│               │
└───────────────┘

A face therefore answers a simple question:

What region is enclosed by this loop?


2. Faces Consume Loops

The geometric hierarchy is intentionally simple:

point(...)
    ↓
edge(point, point)
    ↓
loop(edge, edge, ...)
    ↓
face(loop)

A face consumes loops.

It does not consume points directly.

It does not consume edges directly.

This separation keeps each primitive responsible for one level of structure:

Point
  └── position

Edge
  └── connectivity

Loop
  └── boundary

Face
  └── surface

Each layer builds on the previous layer without duplicating information.


3. Faces Use the Same Binding Model

No special geometry syntax is introduced for surfaces.

A loop receives a name:

l0<loop($e0, $e1, $e2, $e3)>

A face references that name:

f0<face($l0)>

The same rule applies throughout MatterScript:

Define once. Reference many.

The $ sigil continues to mean:

Look up an existing binding.

This consistency is important.

The language does not introduce a separate way of describing surfaces.

Faces participate in the same naming and composition system as every other entity.


4. Faces Become Geometry Entities

When a face is resolved, it becomes a new geometry entity inside the SpatialGraph.

Conceptually:

MatterScript

f0<face($l0)>
        │
        ▼
Geometry Resolver
        │
        ▼
 SpatialGraph
        │
        ▼
    FaceId

The source-level name:

$f0

provides a stable reference to that geometric surface.

This allows faces to participate in later processing stages:

  • triangulation
  • mesh generation
  • placement
  • delay synthesis
  • sensor mapping
  • hardware projection

The face is therefore more than a drawing primitive.

It becomes a named region within the geometry graph.


5. A Face Is More Than a Polygon

Traditional graphics systems often describe surfaces as lists of coordinates:

[(0,0), (1,0), (1,1), (0,1)]

MatterScript takes a different approach.

The surface is constructed gradually:

coordinates
      ↓
points
      ↓
edges
      ↓
loops
      ↓
faces

The advantage is that every intermediate structure remains visible and addressable.

The language can therefore express:

  • position,
  • connectivity,
  • boundary,
  • and surface

as separate concepts.

This makes geometry compositional rather than monolithic.


6. A Face Does Not Reconstruct Its Boundary

The face:

f0<face($l0)>

does not create new edges.

It does not recreate points.

It simply refers to an already validated loop.

Conceptually:

Face
  │
  ▼
Loop
  │
  ▼
Edges
  │
  ▼
Points

Each level owns a specific responsibility.

The face assumes that the loop already forms a valid boundary.

This keeps the system modular.


7. Surface Identity

Two faces may occupy neighboring regions.

For example:

+-----+-----+
|  F0 |  F1 |
+-----+-----+

Each face has its own identity:

f0
f1

Even if they share geometric boundaries, the surfaces remain distinct entities.

This is important because surfaces often represent meaningful regions:

  • walls,
  • floors,
  • sensor zones,
  • computational cells,
  • hardware regions,
  • physical materials.

The face is therefore not merely geometry.

It is a named spatial object.


8. Faces Compose Larger Structures

A single face describes one surface.

Many faces can describe more complex structures.

For example:

Cube

   +------+
  /|     /|
 +------+ |
 | |    | |
 | +----|-+
 |/     |/
 +------+

The cube is not a special primitive.

It is composed from faces:

Face
 + Face
 + Face
 + Face
 + Face
 + Face

The same construction applies at larger scales:

faces
   ↓
shells
   ↓
objects
   ↓
assemblies

The language scales through composition.

No new syntax is required.


9. Faces in Three Dimensions

A face is not limited to flat two-dimensional examples.

The same primitive applies in three-dimensional geometry.

For example:

p0<point(0,0,0)>
p1<point(1,0,0)>
p2<point(1,1,0)>
p3<point(0,1,0)>

...

f0<face($l0)>

The face still represents:

A surface enclosed by a loop.

The coordinates simply happen to exist in three-dimensional space.

This is why the same vocabulary works for both:

spatial2d
spatial3d

The underlying ideas remain unchanged.


10. Faces and Triangulation

Most rendering systems ultimately operate on triangles.

MatterScript intentionally does not expose triangles directly.

Instead:

Face
   ↓
Triangulation
   ↓
Triangles

For example:

Quadrilateral Face

+---------+
|         |
|         |
+---------+

may become:

+---------+
|\        |
| \       |
|  \      |
+---------+

The triangulation step converts high-level surfaces into low-level mesh primitives.

This is important because the programmer works with meaningful geometry while the exporter produces efficient representations.


11. Faces and Mesh Generation

The geometry pipeline is now complete:

MatterScript Source
        │
        ▼
IPL Parser
        │
        ▼
Spatial Resolver
        │
        ├── Points
        ├── Edges
        ├── Loops
        └── Faces
        │
        ▼
Triangulation
        │
        ▼
TriangulatedMesh
        │
        ▼
PLY

The face is the final topological primitive before mesh generation.

Everything below it becomes implementation detail.


12. Faces and Physical Computation

Faces become particularly interesting when geometry is connected to computation.

A face can represent:

  • a sensor region,
  • a hardware tile,
  • an FPGA area,
  • a robotic workspace,
  • a simulation cell,
  • a thermal region,
  • a biological membrane.

For example:

Face
   ↓
Computational Region

This allows geometry to become part of the execution model rather than merely an output format.

A face is no longer just something that can be drawn.

It becomes a region where relationships exist.


13. Faces and Placement

Later chapters will use faces as targets for placement:

cell
   ↓
face
   ↓
physical location

This enables statements such as:

Place this computation within this region.

The face becomes part of the bridge between:

geometry
      ↔
computation

This is one of MatterScript’s central goals.


14. The Complete Example

Square[()()
    @domain(spatial2d)

    p0<point(0.0, 0.0)>
    p1<point(1.0, 0.0)>
    p2<point(1.0, 1.0)>
    p3<point(0.0, 1.0)>

    e0<edge($p0, $p1)>
    e1<edge($p1, $p2)>
    e2<edge($p2, $p3)>
    e3<edge($p3, $p0)>

    l0<loop($e0, $e1, $e2, $e3)>

    f0<face($l0)>
]

The structure is:

Points
   ↓
Edges
   ↓
Loop
   ↓
Face
   ↓
Mesh

Each layer adds meaning:

Point
  = position

Edge
  = connectivity

Loop
  = boundary

Face
  = surface

15. Why Faces Matter

A graphics system might stop at triangles.

MatterScript stops at relationships.

The programmer does not describe:

How should this be triangulated?

Instead they describe:

What surfaces exist?

The system derives the implementation details.

This allows geometry to remain:

  • meaningful,
  • compositional,
  • reusable,
  • physically interpretable.

The face therefore becomes an important bridge between:

space
geometry
computation
hardware

16. Looking Ahead

With faces, the core topological hierarchy is complete:

POINT
   ↓
EDGE
   ↓
LOOP
   ↓
FACE

From here, the system can move toward:

faces
   ↓
meshes
   ↓
placement
   ↓
hardware

The next chapters explore how geometry becomes physical computation:

  • Cell Placement
  • Conformal Projection
  • Delay Synthesis
  • Sensor Placement
  • Hardware Fidelity

The geometric language is now complete enough to describe not only shapes, but the physical structure of computation itself.

Drafting in progress…

Meshes

Vertices establish position.

Edges establish connectivity.

Loops establish boundaries.

Faces establish surfaces.

A mesh turns those surfaces into a discrete representation that can be processed, exported, inspected, and eventually used as the foundation for physical computation.

In MatterScript, the mesh is not the starting point.

It is the result of lowering the higher-level geometric structure into a collection of computational primitives:

 POINT
   │
   │ position
   ▼
 EDGE
   │
   │ connectivity
   ▼
 LOOP
   │
   │ boundary
   ▼
 FACE
   │
   │ surface
   ▼
 MESH
   │
   │ discretization
   ▼
  PLY

This distinction is fundamental.

MatterScript describes geometry in terms of meaningful structures. The mesh is generated afterward as a representation of that structure.

A face describes a surface. A mesh discretizes that surface into computable primitives.


1. Why Have a Mesh?

A face is a useful geometric concept:

f0<face($l0)>

It tells us that a particular loop represents a surface.

But many downstream systems do not operate directly on arbitrary surfaces.

They operate on discrete elements.

A mesh provides that discrete representation.

Conceptually:

High-level geometry

       FACE
        │
        ▼
   continuous
    surface
        │
        ▼
   triangulation
        │
        ▼
Low-level geometry

   ▲───────▲
   │\      │
   │ \     │
   │  \    │
   │   \   │
   ▲───────▲

The mesh therefore acts as an interface between the geometric model and systems that need explicit vertices and triangles.


2. MatterScript Does Not Start with Triangles

A conventional mesh-oriented workflow might begin with something like:

vertices
+
triangle indices

MatterScript starts higher up.

The programmer describes:

points
edges
loops
faces

and the system derives the triangle representation.

This gives the source language a more meaningful vocabulary.

Instead of writing:

Split this quadrilateral into these two triangles.

the programmer writes:

This is a surface bounded by this loop.

Triangulation becomes an implementation step.

MatterScript

face
  │
  ▼
triangulate
  │
  ▼
triangles

The distinction allows the source representation to remain independent of the particular mesh representation eventually produced.


3. The Spatial Graph Is the Source Geometry

The geometry resolver builds a SpatialGraph.

That graph contains the geometric entities constructed from the MatterScript source:

SpatialGraph

  Points
     │
     ├── Edges
     │
     ├── Loops
     │
     └── Faces

The source-level names remain associated with the corresponding geometry handles through the geometry binding table.

For example:

p0<point(0.0, 0.0)>
p1<point(1.0, 0.0)>

e0<edge($p0, $p1)>

becomes part of the graph as actual geometry entities rather than remaining merely as source text.

The mesh is generated from this graph.

That makes the architecture:

MatterScript
     │
     ▼
SpatialGraph
     │
     ▼
TriangulatedMesh

rather than:

MatterScript
     │
     ▼
triangle list

This is an important architectural distinction.


4. Triangulation Is a Lowering Step

The current implementation provides a spatial.triangulate stage:

SpatialGraph
     │
     ▼
spatial.triangulate
     │
     ▼
TriangulatedMesh

Triangulation converts the higher-level surfaces into triangles.

For a simple quadrilateral face:

+---------+
|         |
|         |
+---------+

the resulting mesh can contain:

+---------+
|\        |
| \       |
|  \      |
|   \     |
+---------+

The important point is that the diagonal is an artifact of triangulation.

It was not part of the original MatterScript geometry description.

The source described a face.

The mesh generator chose a discrete representation of that face.


5. A Simple Example

Consider the smallest useful surface:

Square[()()
    @domain(spatial2d)

    p0<point(0.0, 0.0)>
    p1<point(1.0, 0.0)>
    p2<point(1.0, 1.0)>
    p3<point(0.0, 1.0)>

    e0<edge($p0, $p1)>
    e1<edge($p1, $p2)>
    e2<edge($p2, $p3)>
    e3<edge($p3, $p0)>

    l0<loop($e0, $e1, $e2, $e3)>

    f0<face($l0)>
]

The source describes:

4 points
4 edges
1 loop
1 face

The triangulation stage turns the single quadrilateral face into:

2 triangles

over the same four vertices.

Conceptually:

Face

p0 ───────── p1
│          /  │
│        /    │
│      /      │
│    /        │
p3 ───────── p2


Mesh

Triangle 1: p0, p1, p3
Triangle 2: p1, p2, p3

The exact triangulation strategy is an implementation concern. The important architectural fact is that the surface existed before the triangles did.


6. The Vertices Survive the Conversion

The mesh does not need to invent a new set of positions for every triangle.

In the simple quadrilateral example, triangulation produces two triangles over the same four vertices.

That means:

SpatialGraph
    │
    ├── p0
    ├── p1
    ├── p2
    └── p3
         │
         ▼
TriangulatedMesh
    │
    ├── Triangle
    └── Triangle

The triangles reference the mesh’s vertex representation.

This is one reason it is useful to distinguish the geometric graph from the triangulated representation.

The graph expresses the geometry.

The mesh expresses a discrete computational representation of that geometry.


7. A Mesh Is Not Just a Collection of Points

A point list alone does not tell a mesh viewer how surfaces are connected.

For example:

p0
p1
p2
p3

contains positions, but not topology.

A mesh adds relationships between those positions:

vertices
   +
triangle connectivity

Conceptually:

Vertices:

p0    p1
       
p3    p2


Triangles:

p0 ───── p1
 │      / │
 │    /   │
 │  /     │
 p3 ───── p2

The mesh therefore combines geometric position with explicit discrete surface connectivity.

This makes it suitable for systems that need to process individual elements rather than reason about an abstract face.


8. From Face to Triangles

The complete lowering process can be summarized as:

Face
  │
  │ bounded by
  ▼
Loop
  │
  │ composed of
  ▼
Edges
  │
  │ connecting
  ▼
Points

and then, in the opposite direction for representation:

Points + Faces
       │
       ▼
 Triangulation
       │
       ▼
Triangles

The source geometry and the mesh therefore describe the same physical structure at different levels.

HIGH LEVEL

"this region is a surface"


LOW LEVEL

"these vertices and triangles represent that surface"

MatterScript keeps those levels separate.


9. The Complete Pipeline

The actual implementation pipeline is deliberately divided into independently testable stages:

.ms.ipl source
      │
      ▼
ipl_parser.parse
      │
      ▼
spatial_domain.resolveSpatialBindings
      │
      ▼
SpatialGraph
      │
      ▼
spatial.triangulate
      │
      ▼
TriangulatedMesh
      │
      ▼
spatial.exportPly
      │
      ▼
binary PLY

The parser itself remains ordinary IPL parsing.

The spatial resolution pass interprets recognized geometry calls and constructs the spatial graph.

Triangulation converts that graph into a mesh.

PLY export serializes the mesh.

Each stage therefore has a clearly defined responsibility.


10. Parsing Does Not Produce the Mesh

This separation is worth emphasizing.

When MatterScript encounters:

p0<point(0.0, 0.0)>

the parser does not immediately create a mesh vertex.

It parses an ordinary IPL fill whose expression is a structured call.

Likewise:

f0<face($l0)>

is initially just parsed syntax.

The spatial resolution pass gives those calls their geometry-specific meaning when the enclosing definition is scoped to spatial2d or spatial3d.

The architecture is therefore:

        Syntax
          │
          ▼
       IPL AST
          │
          ▼
   Spatial Resolution
          │
          ▼
    Spatial Graph
          │
          ▼
      Triangulation
          │
          ▼
         Mesh

The mesh is several semantic levels downstream from parsing.


11. Mesh Generation Is Domain-Specific

Spatial geometry functions are available when the definition declares:

@domain(spatial2d)

or:

@domain(spatial3d)

The mesh exporter uses that same domain information to determine which definitions should be processed as geometry.

Definitions without a spatial domain are not sent through the mesh path.

Likewise, spatial1d is not currently a geometry domain.

This means one .ms.ipl source file can contain different kinds of definitions:

MatterScript file
       │
       ├── causal definitions
       │       ↓
       │     VHDL path
       │
       └── spatial definitions
               ↓
            mesh path

The routing is determined by the definition’s domain.


12. Mesh Export

Once the TriangulatedMesh exists, it can be serialized.

The current implementation provides:

spatial.exportPly

which produces binary PLY data.

The resulting file contains the mesh representation:

vertices
+
triangular faces

For the simple quadrilateral example, the exported mesh contains:

4 vertices
2 triangular faces

and can be opened by mesh inspection tools such as MeshLab.

The important distinction is:

MatterScript
    ↓
SpatialGraph
    ↓
TriangulatedMesh
    ↓
PLY

PLY is an output representation.

It is not the language’s fundamental geometry model.


13. Why PLY?

PLY provides a convenient interchange boundary between MatterScript and external geometry tools.

The MatterScript pipeline can produce an actual binary mesh file:

<definition-name>.ply

under the run’s workspace.

That means the generated geometry can leave the compiler/runtime environment and enter an ordinary mesh-processing workflow.

For example:

MatterScript
     ↓
    PLY
     ↓
  MeshLab
     ↓
inspection / validation

This gives the spatial compiler a concrete artifact that can be examined independently of the compiler itself.


14. Meshes Make Geometry Testable

A particularly useful property of the mesh boundary is that it creates an observable artifact.

A MatterScript definition can be transformed into a PLY file and then inspected independently.

The pipeline can therefore be tested at multiple levels:

Source
  │
  ├── parser tests
  │
  ├── geometry-resolution tests
  │
  ├── topology tests
  │
  ├── triangulation tests
  │
  └── PLY export tests

This is valuable because each stage can be verified without requiring the entire system to be treated as one opaque operation.

The mesh becomes a concrete boundary between semantic geometry and external representation.


15. Geometry-Only Definitions

A geometry definition does not need ordinary IPL sources or destinations.

For example:

Cube[()()
    @domain(spatial3d)

    ...
]

can exist specifically to produce geometry.

The mesh exporter does not require the definition to have ordinary causal destinations.

This reflects an important distinction:

Causal definition

inputs → computation → outputs


Geometry definition

positions → topology → surface → mesh

Both use the same IPL source language.

They simply make use of different consumers of the parsed definition.


16. A Complete Three-Dimensional Example

A cube demonstrates why the distinction between geometry and mesh becomes increasingly useful.

The source can define eight points:

p0<point(0.0, 0.0, 0.0)>
p1<point(1.0, 0.0, 0.0)>
p2<point(1.0, 1.0, 0.0)>
p3<point(0.0, 1.0, 0.0)>

p4<point(0.0, 0.0, 1.0)>
p5<point(1.0, 0.0, 1.0)>
p6<point(1.0, 1.0, 1.0)>
p7<point(0.0, 1.0, 1.0)>

Each face is then constructed from its own four-edge loop.

For example, the bottom face:

e_b0<edge($p0, $p1)>
e_b1<edge($p1, $p2)>
e_b2<edge($p2, $p3)>
e_b3<edge($p3, $p0)>

l_bottom<loop($e_b0, $e_b1, $e_b2, $e_b3)>
f_bottom<face($l_bottom)>

The other five faces follow the same pattern.

The source therefore describes:

8 points
6 faces
24 edge bindings
6 loops

with each face owning its own four edge bindings, including geometrically coincident segments.

The triangulation stage can then lower those surfaces into a triangle mesh.

The source remains understandable as a cube.

The mesh remains understandable as a collection of triangles.

Both representations describe the same structure at different levels.


17. The Mesh as a Computational Boundary

The mesh is particularly important because it provides a representation that other systems can consume.

Once geometry has been reduced to explicit vertices and triangles, it can be used by:

  • mesh viewers,
  • geometry processors,
  • spatial algorithms,
  • simulation systems,
  • fabrication pipelines,
  • physical layout tools.

The mesh therefore becomes a bridge:

MatterScript
     │
     ▼
semantic geometry
     │
     ▼
discrete mesh
     │
     ├── visualization
     ├── simulation
     ├── fabrication
     └── physical computation

The important architectural principle is that these downstream systems do not need to understand MatterScript’s entire geometric construction language.

They can consume the representation appropriate to their task.


18. The Mesh Is Not the End

It would be tempting to think of:

MatterScript → PLY

as the purpose of the spatial system.

It is not.

PLY is simply the current concrete endpoint of the implemented pipeline.

The more important transformation is:

source description
       ↓
semantic geometry
       ↓
computable geometry

The mesh makes geometry explicit enough for another computational system to operate on it.

This becomes particularly important when geometry is eventually connected to MatterScript’s other concepts.

A spatial region could become a placement target.

A distance could become a delay.

A surface could become a sensor domain.

A mesh could become the physical substrate on which computation is mapped.

The mesh is therefore a boundary between describing physical structure and computing with physical structure.


19. From Mesh to Physical Computation

The larger direction of the MatterScript spatial system can now be seen:

Geometry

POINT
  ↓
EDGE
  ↓
LOOP
  ↓
FACE
  ↓
MESH

Computation

MESH
  ↓
PLACEMENT
  ↓
PROJECTION
  ↓
DELAY
  ↓
SENSING
  ↓
HARDWARE

The first half constructs the physical model.

The second half uses that model.

This is where MatterScript begins to move beyond conventional geometry processing.

A mesh is not merely something to render.

It can become a coordinate system for computation.


20. One Language, Multiple Representations

The same source definition can move through several representations:

MatterScript source
        │
        ▼
      IPL AST
        │
        ▼
   SpatialGraph
        │
        ▼
 TriangulatedMesh
        │
        ▼
      PLY file

Each representation answers a different question.

MatterScript

What did the programmer describe?

SpatialGraph

What geometric entities and relationships exist?

TriangulatedMesh

How can those surfaces be represented as discrete triangles?

PLY

How can that mesh be serialized for another tool?

The representations are therefore not competing models.

They are layers.


21. The Geometry Pipeline Is Compositional

The complete Part V geometry construction sequence can now be summarized:

@domain(spatial2d | spatial3d)
              │
              ▼
           point()
              │
              ▼
            edge()
              │
              ▼
            loop()
              │
              ▼
            face()
              │
              ▼
         triangulation
              │
              ▼
          TriangulatedMesh
              │
              ▼
             PLY

Every stage consumes the structure produced by the stage before it.

No new representation has to be invented for every step.

The same names, the same $ reference convention, and the same define-once binding discipline continue throughout the construction.

That continuity is one of the most important properties of the design.


22. Why the Mesh Layer Matters

The mesh is where MatterScript’s abstract geometric relationships become a form that ordinary computational machinery can consume.

The progression is:

point
  = position

edge
  = connectivity

loop
  = boundary

face
  = surface

mesh
  = discrete representation

This is more than a convenient export pipeline.

It establishes a boundary between two ways of thinking about geometry:

SEMANTIC

"What is this structure?"


COMPUTATIONAL

"How do I represent and process it?"

MatterScript works on the semantic side.

The mesh provides the computational bridge.


23. Looking Ahead: Computing Within Geometry

With the mesh layer in place, Part V can now change direction.

The first chapters constructed space:

Spatial Domains
      ↓
   Vertices
      ↓
    Edges
      ↓
    Loops
      ↓
    Faces
      ↓
    Meshes

The remaining chapters ask what computation can do with that space:

Meshes
   ↓
Cell Placement
   ↓
Conformal Projection
   ↓
Delay Synthesis
   ↓
Sensor Placement
   ↓
Hardware Fidelity

The fundamental transition is:

First we construct space. Then we compute within it.

The mesh is the boundary between those two worlds.

It takes the physical structure described by MatterScript and gives it a discrete, inspectable, computational form.

That is what makes geometry more than an output.

It makes geometry available as a substrate for computation.

Drafting in progress…

Cell Placement

A mesh gives MatterScript a representation of physical space.

Cell placement gives that space computational meaning.

A spatial domain can be divided into discrete locations, and each location can become a computational cell. The behavior of the entire system is then determined by local relationships between neighboring cells.

This is the point where MatterScript connects directly to one of the most important ideas in A New Kind of Science:

Complex global behavior can arise from simple local rules.

The programmer does not necessarily need to describe the behavior of the entire system.

Instead, the programmer describes:

  1. the space in which computation occurs,
  2. the state of each cell,
  3. the neighborhood visible to a cell,
  4. and the rule that determines its next state.

The system then evolves.

SPACE
  │
  ▼
CELLS
  │
  ▼
LOCAL NEIGHBORHOODS
  │
  ▼
LOCAL RULE
  │
  ▼
GLOBAL BEHAVIOR

This is the fundamental connection between MatterScript and the computational universe explored by Stephen Wolfram’s A New Kind of Science.


1. From Geometry to Computation

The previous chapters constructed geometry:

Point
  ↓
Edge
  ↓
Loop
  ↓
Face
  ↓
Mesh

Cell placement adds another dimension:

Mesh
  ↓
Cells
  ↓
State
  ↓
Evolution

The distinction is important.

A mesh tells us where things are.

A cell tells us where computation happens.

For example:

+---+---+---+---+
|   |   |   |   |
+---+---+---+---+
|   |   |   |   |
+---+---+---+---+
|   |   |   |   |
+---+---+---+---+

Each square can become a computational location.

The geometry establishes the spatial relationships.

The cell model establishes the computational relationships.

MatterScript therefore allows the same underlying idea of locality to be expressed spatially and computationally.


2. A Cellular Space

MatterScript can declare a spatial cellular domain:

@domain(spatial2d, size: [300, 500])

The size establishes the extent of the cellular space.

Conceptually:

          500
    ┌───────────────┐
    │               │
    │               │
300 │   CELLULAR    │
    │     SPACE     │
    │               │
    │               │
    └───────────────┘

The important abstraction is not the rectangle itself.

It is the fact that the domain contains a collection of discrete computational locations.

domain
   │
   ├── cell
   ├── cell
   ├── cell
   ├── cell
   └── ...

Each cell occupies a particular location within the domain.

That location gives the cell a neighborhood.

The neighborhood gives the cell access to local information.

And the local information determines its evolution.


3. The Cell Is a Local Computational Entity

Consider a one-dimensional neighborhood:

[ Left ][ Center ][ Right ]

A cell observes its local neighborhood:

      neighborhood
   ┌─────┬─────┬─────┐
   │  L  │  C  │  R  │
   └─────┴─────┴─────┘

The next state of the center cell is determined by those states.

Conceptually:

       L   C   R
       │   │   │
       └───┼───┘
           ▼
       local rule
           │
           ▼
       new C state

This is a cellular automaton.

The important property is locality.

The cell does not need to know the state of the entire universe.

It needs only the portion of the universe included in its neighborhood.


4. The Generate Directive

MatterScript expresses this local evolution rule with the @generate directive.

A simple example is:

ca100[()()

@domain(spatial2d, size: [300, 500])

@generate {
  // Format: [ Left , Center , Right ] -> New Center State

  [*, 1, 5]: 4

  [3, 2, 4]: 6

  // Unmentioned permutations retain their center state.
}
  :
]

The structure is deliberately compact.

@generate
    │
    ▼
neighborhood
    │
    ▼
next state

The rule:

[3, 2, 4]: 6

means:

left   = 3
center = 2
right  = 4

        ↓

new center = 6

The rule describes the transformation of one local configuration.

It does not describe the entire grid.


5. Wildcards

The generate language also permits wildcard matching.

For example:

[*, 1, 5]: 4

means that the left-hand state can be any value.

Conceptually:

[0, 1, 5] → 4
[1, 1, 5] → 4
[2, 1, 5] → 4
[3, 1, 5] → 4
...

The center and right states remain constrained:

Center = 1
Right  = 5

while:

Left = anything

This provides a compact way to express families of local transitions without enumerating every permutation individually.


6. The Default Rule

Not every possible neighborhood needs to be explicitly written.

For example:

[3, 2, 4]: 6

does not say what should happen for:

[0,0,0]

or:

[1,4,2]

or any other unmentioned configuration.

The generate semantics provide a default:

Unmentioned permutations retain their center state.

Thus:

[0, 0, 0] → 0

and:

[1, 4, 2] → 4

unless an explicit or wildcard rule matches those neighborhoods.

This makes the rule specification sparse.

The programmer specifies what changes rather than being forced to enumerate everything that remains unchanged.


7. Local Rules, Global Systems

The remarkable property of a cellular automaton is that a tiny rule can govern an enormous number of cells.

Suppose the domain contains thousands of cells.

The programmer still defines the rule once:

[Left, Center, Right] → New Center

The rule is then applied throughout the cellular space.

Conceptually:

        local rule
             │
      ┌──────┼──────┐
      ▼      ▼      ▼
    cell    cell   cell
      │      │      │
      ▼      ▼      ▼
    state   state  state

The same rule is reused at every applicable location.

This is one of the most important differences between describing a system globally and describing it locally.

Instead of:

Here is the complete behavior of the 300 × 500 system.

we say:

Here is what any cell does when it encounters this neighborhood.

The global behavior emerges from repeated local application.


8. The NKS Connection

This is where MatterScript enters the conceptual territory of A New Kind of Science.

NKS treats cellular automata as a particularly important class of computational systems because extremely simple rules can produce unexpectedly rich behavior.

The essential structure is:

simple rule
     +
simple initial condition
     +
repeated local evolution
     ↓
complex global behavior

MatterScript provides a language-level mechanism for expressing exactly this relationship.

The programmer describes the local rule.

The cellular domain provides the space.

Repeated application provides the evolution.

The resulting behavior is discovered by running the system.


9. From Programming Behavior to Discovering Behavior

Traditional programming generally begins with a desired global behavior.

For example:

"Sort these values."

"Render this image."

"Calculate this result."

The programmer decomposes the desired behavior into instructions.

Cellular computation allows a different approach.

We can specify:

"What happens locally?"

and then observe:

"What happens globally?"

The difference is profound.

TRADITIONAL

desired behavior
       ↓
algorithm
       ↓
execution


CELLULAR

local rule
       ↓
repeated evolution
       ↓
emergent behavior

The second approach is much closer to the experimental methodology of NKS.

Instead of always designing the final behavior first, we can explore what simple computational rules produce.


10. Rule Space Becomes a Programming Space

Once the system is described by a local rule, the rule itself becomes an object of exploration.

Imagine varying:

[*, 1, 5]: 4

into another rule:

[*, 1, 5]: 3

or adding another transition:

[2, 3, *]: 7

Each change creates a different dynamical system.

We can therefore think of the space of possible programs as:

             RULE SPACE

       ┌──────┬──────┬──────┐
       │ R001 │ R002 │ R003 │
       ├──────┼──────┼──────┤
       │ R004 │ R005 │ R006 │
       ├──────┼──────┼──────┤
       │  ... │  ... │  ... │
       └──────┴──────┴──────┘

MatterScript does not have to prescribe which rule is interesting.

It provides a mechanism for generating and running the rules.

This turns the language into an experimental environment for exploring computational behavior.


11. Emergence

The central phenomenon is emergence.

A single cell follows a simple rule.

A large collection of cells can exhibit behavior that is not obvious from inspecting any individual transition.

LOCAL

[1, 2, 1] → 3


GLOBAL

many cells
    ↓
repeated application
    ↓
patterns
    ↓
structures
    ↓
behavior

The global pattern is not necessarily written anywhere in the source.

It emerges from the repeated interaction of local computations.

This is one reason cellular automata are so important to MatterScript.

They provide a concrete computational model in which:

structure can emerge from locality.


12. Space Is Part of the Program

This changes the meaning of a program.

In conventional code, the location of an instruction is usually an implementation detail.

In a cellular system, location is part of the computation.

Consider:

cell A
cell B
cell C

The relationship:

A is next to B

can determine whether information flows between them.

The program therefore contains not only a rule:

if neighborhood = X
then state = Y

but also a spatial structure:

this cell
is adjacent to
those cells

MatterScript can represent both.

That is the deeper connection between the geometry system and cellular computation.


13. Geometry Provides Locality

The geometry chapters established:

points
edges
loops
faces
meshes

These structures establish spatial relationships.

Cell placement uses those relationships to define computational neighborhoods.

The conceptual transition is:

GEOMETRY

where are things?

        ↓

TOPOLOGY

what is connected to what?

        ↓

CELLULAR COMPUTATION

what information can each location see?

        ↓

EVOLUTION

how does the system change?

This is why Cell Placement belongs immediately after Meshes.

It is the first chapter where the geometry becomes an active computational substrate.


14. Locality Is a Computational Primitive

In ordinary programming, locality is often treated as a property of implementation.

In MatterScript, locality can become part of the program itself.

A cell’s neighborhood defines the information available to it.

For the rule:

[Left, Center, Right] → New Center

the computational boundary is explicit:

        visible
   ┌─────┬─────┬─────┐
   │  L  │  C  │  R  │
   └─────┴─────┴─────┘

The cell cannot arbitrarily inspect every other cell.

Its computation is constrained by locality.

That constraint is not merely an optimization.

It is part of the computational model.


15. The Universe as a Cellular System

NKS encourages a much larger question:

Could simple local computational rules be sufficient to generate the behavior we observe in nature?

MatterScript does not need to answer that question in advance.

Instead, it provides a language in which such questions can become executable experiments.

A cellular model can represent:

local state
     ↓
local interaction
     ↓
local evolution
     ↓
global pattern

This makes cellular automata a bridge between programming and modeling.

The programmer can construct a small local rule and then investigate what kind of world it produces.


16. Theory-Free Exploration

This also connects directly to model-free inference.

Instead of beginning with a predefined equation describing a phenomenon, we can observe transitions:

before                  after

A B C                    A D C
B C A       ─────→       B B A
C A B                    E A B

and ask:

What local rule could have generated this transition?

The inference problem becomes:

observations
     ↓
local transitions
     ↓
candidate rules
     ↓
generated evolution
     ↓
comparison

MatterScript can then become both:

a language for specifying rules

and:

a substrate for testing inferred rules.

This opens a path toward systems that discover local computational laws from observed spatial behavior.


17. Rule Generation and Rule Discovery

The @generate directive is therefore more significant than a convenient cellular-automata syntax.

It defines a boundary between:

RULE SPECIFICATION

and:

RULE EXECUTION

The programmer can explicitly provide:

@generate {
  [*, 1, 5]: 4
  [3, 2, 4]: 6
}

But a future system could potentially produce the same structure from:

observed transitions

The architecture becomes:

                   ┌──────────────┐
                   │ Human author │
                   └──────┬───────┘
                          │
                          ▼
                      @generate
                          │
                          ▼
                      local rule
                          │
                          ▼
                    cellular system
                          ▲
                          │
                     inferred rule
                          │
                   ┌──────┴───────┐
                   │ observations │
                   └──────────────┘

The language can therefore support both programmed computation and eventually discovered computation.


18. Computational Irreducibility

Cellular systems also provide a concrete setting for another important NKS concept: computational irreducibility.

A system may have a very short description:

one simple local rule

while the behavior generated after many iterations may not have an equally simple shortcut.

The only practical way to know what happens may be to let the system evolve.

Conceptually:

small rule
    │
    ▼
iteration 1
    │
    ▼
iteration 2
    │
    ▼
iteration 3
    │
    ▼
...
    │
    ▼
complex state

This changes the role of the programmer.

The programmer is not always deriving the answer analytically.

Sometimes the programmer constructs the computational universe and runs it to find out what happens.

That is a very different programming paradigm.


19. Simple Rules, Large Spaces

The example domain:

@domain(spatial2d, size: [300, 500])

contains a large number of locations while the rule itself remains tiny.

This gives us a useful ratio:

large computational space
          +
small rule description
          ↓
large computational behavior

The source code does not need to scale linearly with the number of cells.

One local rule can govern an entire population.

This is precisely what makes cellular computation attractive as a model of distributed physical systems.


20. Cell Placement as Physical Placement

There is another important interpretation.

A cell is not merely an abstract array element.

It occupies a location.

That means the same conceptual cell can eventually be mapped onto:

software memory
FPGA logic
hardware cells
sensor locations
fabricated structures
physical regions

The abstraction is:

CELL
 │
 ├── state
 ├── neighborhood
 ├── rule
 └── physical location

This is where MatterScript begins to bridge cellular computation and physical computing.

The question becomes not merely:

What does this cell calculate?

but:

Where does this computation physically exist?


21. Cell Placement and Hardware

Suppose a cellular rule is eventually lowered onto physical hardware.

The abstract model might be:

Cell
 │
 ├── input from left
 ├── local state
 ├── input from right
 └── output

A physical implementation could become:

┌───────┐    ┌───────┐    ┌───────┐
│ Cell  │───▶│ Cell  │───▶│ Cell  │
│   A   │    │   B   │    │   C   │
└───────┘    └───────┘    └───────┘

The spatial relationship becomes an actual interconnection.

This is exactly where the distinction between conventional software and MatterScript begins to disappear.

The program’s topology can become the hardware’s topology.


22. A Cellular Automaton Is a Distributed Program

A useful way to think about the @generate directive is:

It defines a program that is replicated across space.

Instead of:

one program
    ↓
one execution

we have:

one local program
    ↓
many spatial instances
    ↓
distributed evolution

Each cell executes the same local relationship.

The global system is the composition of those local processes.

This makes cellular automata a natural intermediate model between software and physical systems.


23. From Cellular Automata to NKS

The NKS connection can now be stated directly.

MatterScript provides the machinery to explore several ideas central to computational experiments with cellular systems:

local rules
    ↓
simple programs
    ↓
large state spaces
    ↓
repeated evolution
    ↓
emergence
    ↓
complexity
    ↓
computation

The important methodological shift is:

Do not always begin by asking how to program the desired answer. Begin by asking what behavior a simple rule generates.

This turns programming into experimentation.

The programmer becomes, in part:

designer
   +
observer
   +
experimentalist

24. A MatterScript Cellular Experiment

The complete example is deliberately small:

ca100[()()

@domain(spatial2d, size: [300, 500])

@generate {
  // Format: [ Left , Center , Right ] -> New Center State

  // Wildcard rule
  [*, 1, 5]: 4

  // Explicit rule
  [3, 2, 4]: 6

  // Unmentioned permutations retain their center state.
}
  :
]

Yet this tiny definition establishes an entire computational universe:

300 × 500 spatial domain
        │
        ▼
discrete cells
        │
        ▼
local neighborhoods
        │
        ▼
local transition rules
        │
        ▼
repeated evolution
        │
        ▼
emergent global behavior

The source code describes the rule.

The system discovers the behavior.


25. From Cell Placement to Space-Time

Cell placement introduces a second dimension to the meaning of a program.

We now have:

SPACE
  │
  ▼
cell location

and:

TIME
  │
  ▼
cell evolution

Together:

              TIME
                │
                ▼
        ┌───────────────┐
        │       •       │
        │     • • •     │
        │   • • • • •   │
        │ • • • • • • • │
        └───────────────┘
                ▲
                │
              SPACE

A cellular automaton is therefore naturally a space-time computation.

Each iteration creates another state of the spatial system.

The complete computation is the history of those states.


26. The Geometry-to-NKS Bridge

The Part V trajectory can now be seen as one continuous construction:

Spatial Domain
      │
      ▼
   Vertices
      │
      ▼
    Edges
      │
      ▼
    Loops
      │
      ▼
    Faces
      │
      ▼
    Meshes
      │
      ▼
Cell Placement
      │
      ▼
 Local Rules
      │
      ▼
Cellular Evolution
      │
      ▼
Emergent Behavior

The first half establishes where.

The second half establishes what happens there.

That is the fundamental bridge between geometry and computation.


27. Why Cell Placement Matters to MatterScript

Cell placement is not simply another feature added to the language.

It establishes a computational model in which:

  • space is explicit,
  • locality is explicit,
  • state is distributed,
  • rules are local,
  • evolution is repeated,
  • and global behavior can emerge.

This provides MatterScript with a direct path into the enormous design space of cellular automata and the broader computational perspective of NKS.

The programmer can begin with something as small as:

[Left, Center, Right] → New Center

and end with a system containing hundreds of thousands of interacting computational locations.

That is an extraordinary amount of behavior generated from an extraordinarily small description.


28. Looking Ahead: Computation Becomes Physical

Cell placement gives us a computational substrate.

The next question is:

What happens when the abstract cellular space must conform to an actual physical structure?

That leads naturally to Conformal Projection.

The trajectory becomes:

CELLULAR SPACE
      │
      ▼
PHYSICAL GEOMETRY
      │
      ▼
CONFORMAL PROJECTION
      │
      ▼
CELLS ON A SURFACE
      │
      ▼
PHYSICAL COMPUTATION

At that point, the NKS cellular universe is no longer confined to an abstract grid.

Its cells can become associated with actual geometry.

The program begins with a local rule.

The geometry determines where the rule lives.

Time determines how it evolves.

And the resulting structure can ultimately become physical machinery.

That is the deeper promise of MatterScript:

A program can describe not only what computation does, but where computation exists, how it evolves through time, and how its local interactions become a physical system.

Drafting in progress…

Conformal Projection: Mapping Computation onto Geometry

Cell placement gives MatterScript a way to turn space into a computational substrate.

A spatial domain establishes where cells exist. A local rule establishes how those cells interact. Repeated application of the rule produces behavior across the domain.

But the computational space does not have to remain a flat rectangle.

A computational lattice can be mapped onto a surface.

A surface can be curved.

A curved surface can belong to a physical object.

And once the computational structure is mapped onto physical geometry, the distinction between where computation happens and what computation does begins to disappear.

This is the purpose of conformal projection.

Conformal projection transports a computational neighborhood structure onto geometry while attempting to preserve the local relationships that make the computation meaningful.

The important idea is not simply projection as visualization.

It is projection as placement.


From Flat Cells to Shaped Space

The cellular domain introduced in the previous chapter is conceptually simple:

+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+
|   |   |   |   |   |
+---+---+---+---+---+

Each location represents a computational cell.

A neighborhood might be defined by the cells immediately to the left and right:

       neighborhood

       left    center    right
         ↓       ↓         ↓
       [ 3 ]   [ 2 ]     [ 4 ]

                  |
                  v

              new state
                  6

The rule does not care about the physical interpretation of the cells.

It only requires that the neighborhood relationships remain meaningful.

This gives us an important separation:

               COMPUTATION

             local rule
                 |
                 v
       +---------------------+
       | neighborhood graph  |
       +---------------------+
                 |
                 v
              geometry

The rule describes relationships.

The geometry describes where those relationships are realized.

Conformal projection provides a way to connect the two.


The Computational Surface

Consider a cellular system defined on a rectangular domain:

        computational domain

      o---o---o---o---o
      |   |   |   |   |
      o---o---o---o---o
      |   |   |   |   |
      o---o---o---o---o
      |   |   |   |   |
      o---o---o---o---o

Now imagine wrapping that computational structure around a cylinder:

              ___________
           .-'           '-.
         .'   o---o---o     '.
        /     |   |   |       \
       |      o---o---o        |
       |      |   |   |        |
        \     o---o---o       /
         '.                 .'
           '-.___________.-'

The underlying computational structure has not fundamentally changed.

The cells still have neighbors.

The local rule still operates on those neighbors.

What has changed is where those cells are located in physical space.

This is the key abstraction:

The computational topology can remain stable while the geometric embedding changes.

MatterScript therefore has two related but distinct descriptions:

          COMPUTATIONAL STRUCTURE
                    |
             neighborhoods
                    |
                    v
              local rules
                    |
                    |
                    v
             GEOMETRIC EMBEDDING
                    |
             coordinates
                    |
                    v
             physical surface

The first describes computation.

The second describes realization.


Mapping Cells onto Geometry

A projection establishes a correspondence between computational coordinates and geometric coordinates.

Conceptually:

computational space              physical space

      (u, v)                         (x, y, z)
        |                                ^
        |                                |
        +---------- projection ----------+

A cell at computational position (u, v) is assigned a position in geometric space.

For a flat surface this mapping might be nearly trivial:

(u, v) → (x, y, 0)

For a curved surface, the same computational coordinates may map to something like:

(u, v) → (x(u,v), y(u,v), z(u,v))

The resulting coordinates can describe a surface that is curved, folded, wrapped, or otherwise shaped.

The cellular system therefore becomes embedded in the geometry.

             CELLULAR SPACE

       +---+---+---+---+
       |   |   |   |   |
       +---+---+---+---+
       |   |   |   |   |
       +---+---+---+---+
       |   |   |   |   |
       +---+---+---+---+
                 |
                 | projection
                 v
             PHYSICAL SPACE

              .-------.
           .-'         '-.
         .'  o---o---o    '.
        /    |   |   |      \
       |     o---o---o       |
        \    |   |   |      /
         '.  o---o---o    .'
           '-._________.-'

This is more than a coordinate transformation.

It establishes a correspondence between a computational domain and a geometric domain.


Preserving Locality

Why does the nature of the projection matter?

Because cellular computation depends on locality.

A cell does not normally require knowledge of the entire system.

It requires information from a small neighborhood.

For example:

[ left ] [ center ] [ right ]

The rule might be:

[*, 1, 5]: 4

The center cell changes according to the states of its neighbors.

If the projection moves those cells onto a physical surface, we want the neighborhood relationship to remain meaningful.

That gives us a fundamental requirement:

A useful geometric projection should preserve computational locality as much as possible.

A projection that dramatically separates neighboring cells can damage the physical interpretation of the computation.

A projection that keeps neighboring cells close together provides a more faithful physical realization.

This creates a hierarchy:

             local rule
                 |
                 v
          neighborhood
                 |
                 v
            cell graph
                 |
                 v
          geometric map
                 |
                 v
          physical layout

Each layer preserves information from the previous one.


What Does “Conformal” Mean?

In geometry, a conformal mapping preserves local angles.

That property is useful because angles describe the local shape of neighborhoods.

A general projection can introduce distortion.

A small square might become:

       square

       +---+
       |   |
       +---+

and after projection become:

       distorted

        /---/
       /   /
       \  /
        \/

A conformal mapping attempts to preserve the local angular structure even when the overall shape changes.

This does not mean that distances are globally preserved.

A map can preserve angles while stretching regions.

That distinction matters.

CONFORMAL

local angles       preserved
local scale        may vary
global distances   not necessarily preserved

For MatterScript, this makes conformal mapping particularly interesting.

The computational system is fundamentally about relationships.

If the local neighborhood structure is important, preserving local geometric relationships can be more useful than preserving absolute distances everywhere.


Geometry Changes; Relationships Persist

This is a recurring principle throughout MatterScript.

A name identifies a thing.

A reference establishes a relationship.

A geometry graph establishes topology.

A cellular domain establishes locality.

Projection establishes an embedding.

The representation changes at each level, but the relationships can remain coherent.

Consider three cells:

A ---- B ---- C

Their computational relationship is:

A ↔ B ↔ C

Now bend the geometry:

A
 \
  B
   \
    C

The physical positions have changed.

The computational relationship has not.

This distinction is crucial:

Geometry is an embedding of relationships, not a replacement for them.

That is why MatterScript can reason about computational structure independently from the geometry used to realize it.


Cell Placement on Curved Surfaces

Suppose a cellular automaton occupies a rectangular domain:

@domain(spatial2d, size: [300, 500])

Conceptually, this establishes a two-dimensional computational field.

Now imagine that field being placed onto a curved surface.

Instead of:

+----------------------+
|                      |
|     COMPUTATION      |
|                      |
+----------------------+

we might have:

          physical surface

             ______
         .-'      '-.
       .'            '.
      /   COMPUTATION  \
     |                  |
      \                /
       '.            .'
         '-.______.-'

The cellular system has acquired a physical shape.

This is where the geometry chapters and the NKS chapters meet.

The geometry tells us where the cells are.

The cellular rule tells us what the cells do.

The projection tells us how the computational world is embedded into the physical world.


A Surface Becomes a Computational Substrate

Once cells are mapped onto a surface, the surface itself can become part of the computation.

This opens a different way of thinking about physical computing.

Traditional programming often begins with an abstract machine:

program
   |
   v
processor
   |
   v
memory

MatterScript can instead begin with a spatial substrate:

geometry
    |
    v
cells
    |
    v
local relationships
    |
    v
local rules
    |
    v
emergent behavior

The machine is no longer necessarily a single processor.

It can be a distributed geometry of interacting locations.

This is one of the most important consequences of combining MatterScript’s geometry model with cellular computation.


Projection as Compilation

Conformal projection can also be understood as a compilation step.

At the source level we may describe:

a cellular domain

At the computational level we have:

cells + neighborhoods + rules

At the geometric level we have:

positions + topology + surfaces

Projection connects these descriptions.

       MatterScript source
                |
                v
       cellular domain
                |
                v
      computational topology
                |
                v
       conformal projection
                |
                v
       geometric embedding
                |
                v
        physical substrate

This is closely related to the broader MatterScript idea that a program can become machinery.

The source description is not merely interpreted.

It can be progressively lowered into a more concrete representation.

The same conceptual process appears elsewhere in the system:

MatterScript
     |
     +--> VHDL
     |
     +--> PLY
     |
     +--> geometry
     |
     +--> physical placement
     |
     +--> machine realization

Conformal projection adds another possible lowering step:

computational space
        ↓
physical space

Geometry as a Computational Constraint

Projection also works in the opposite direction.

Geometry does not merely receive computation.

It constrains computation.

A cell placed on a physical surface has a real location.

That location can determine:

  • which cells are nearby,
  • how far signals must travel,
  • what physical structures can connect,
  • where sensors can be placed,
  • where actuators can be located,
  • how much material is available,
  • and how information propagates through the system.

The geometry therefore becomes part of the problem definition.

       computation
            ↕
         geometry
            ↕
      physical limits

This is fundamentally different from treating geometry as a final rendering step.

The shape of the computational substrate can influence the behavior of the computation itself.


The NKS Connection

This brings us back to the ideas introduced with cellular computation and A New Kind of Science.

A cellular automaton can be defined by a remarkably small local rule.

Yet repeated application of that rule across a large space can produce behavior that is difficult to predict from the rule alone.

             SIMPLE RULE

                 |
                 v

        +----------------+
        |  |  |  |  |  |
        |  |  |  |  |  |
        |  |  |  |  |  |
        +----------------+

                 |
           repeated evolution
                 |
                 v

        COMPLEX GLOBAL BEHAVIOR

Conformal projection adds another dimension to this idea.

The computational universe does not have to be confined to a rectangular grid.

The same principles can be embedded into different geometries.

             LOCAL RULE
                  |
          +-------+-------+
          |       |       |
          v       v       v
        plane   cylinder  surface
          |       |       |
          +-------+-------+
                  |
                  v
        emergent computation

The rule remains local.

The computational substrate changes.

The resulting physical behavior can therefore depend not only on what rule is used, but also on where and how the rule is embedded.

This is an important expansion of the NKS perspective:

The rule space and the geometry space can become coupled experimental dimensions.


Exploring Rule Space and Geometry Space

Traditional cellular automata experimentation varies the rule.

For example:

Rule A
Rule B
Rule C
Rule D
...

MatterScript can potentially vary both rule and geometry:

                RULE SPACE
                    |
       +------------+------------+
       |            |            |
       v            v            v
     Rule A       Rule B       Rule C
       |            |            |
       +------------+------------+
                    |
                    v
              GEOMETRY SPACE
                    |
       +------------+------------+
       |            |            |
       v            v            v
     Plane      Cylinder      Surface

Now an experiment is no longer simply:

What happens when this rule runs?

It can become:

What happens when this rule runs on this geometry?

And then:

What changes when the same rule is embedded in a different geometry?

This is exactly the kind of open-ended experimental space that makes computational geometry more than visualization.


From Cellular Automata to Physical Systems

Once cells occupy physical geometry, a cellular automaton begins to resemble a physical system.

Each cell can represent something physically meaningful:

sensor
logic element
material region
actuator
memory element
particle
chemical site
electrical node

The neighborhood can represent a physical relationship:

electrical proximity
material adjacency
thermal coupling
mechanical contact
signal propagation
chemical interaction

And the local rule can represent a transformation:

state transition
signal response
material change
control decision

The abstract cellular system therefore becomes a template for constructing physical systems.

       CELLULAR MODEL

       [A] [B] [C]
       [D] [E] [F]
       [G] [H] [I]

            ↓

       GEOMETRIC MODEL

        A---B---C
       / \ / \ / \
      D---E---F
       \ / \ / \ /
        G---H---I

            ↓

       PHYSICAL SYSTEM

      sensor → logic → actuator

The exact realization depends on the target hardware or material system.

The computational principle remains the same.


Locality Becomes Physical

In a purely abstract cellular automaton, locality is a mathematical relationship.

In a physical system, locality can become a physical fact.

Two cells may be neighbors because they occupy adjacent positions on a surface.

A signal may propagate from one cell to another because a physical connection exists between them.

A delay may arise because the path between two cells has a measurable length.

This is where the next stages of MatterScript become particularly interesting.

Geometry can begin to determine time.

position
   |
   v
distance
   |
   v
propagation
   |
   v
 delay
   |
   v
state transition

The spatial graph constructed in the earlier chapters therefore provides more than a representation of shape.

It provides the foundation for reasoning about space-time computation.


Projection and Physical Fidelity

A useful MatterScript system should distinguish between different levels of fidelity.

At the most abstract level:

cell A is adjacent to cell B

At a geometric level:

A = (x₁, y₁, z₁)
B = (x₂, y₂, z₂)

At a physical level:

distance(A,B) = d

At a timing level:

propagation(A,B) = Δt

These are not necessarily separate systems.

They can be successive refinements of the same description.

relationship
     ↓
geometry
     ↓
distance
     ↓
delay
     ↓
physical behavior

This is one of the central ambitions of MatterScript:

Preserve the relationship as the description becomes progressively more physical.


A Computational Surface Is More Than a Mesh

The mesh chapter established a distinction between source geometry and its triangulated representation.

That distinction becomes important here.

A mesh may describe the physical surface:

vertices
edges
triangles

The cellular system may describe computation:

cells
neighbors
states
rules

Projection establishes the relationship between them.

       CELLULAR GRAPH
       --------------
       cells
       neighborhoods
       states
            |
            | projection
            v
       GEOMETRIC MESH
       --------------
       vertices
       edges
       faces

The mesh is therefore not necessarily the computation.

It is the geometric substrate onto which computation is placed.

This separation allows the same computational structure to potentially be realized on different physical geometries.


One Computation, Many Geometries

Imagine a cellular rule defined once:

@generate {
  [*, 1, 5]: 4
  [3, 2, 4]: 6
}

The rule itself does not encode a particular physical surface.

That means the same computational idea can potentially be explored on:

flat plane
     |
     v
cylindrical surface
     |
     v
spherical surface
     |
     v
custom mesh
     |
     v
fabricated physical structure

The geometry becomes a parameter of realization.

This is a powerful consequence of separating rule, topology, and embedding.


Projection as a MatterScript Primitive

The broader MatterScript architecture suggests a useful principle:

describe the thing
        ↓
resolve relationships
        ↓
construct structure
        ↓
place structure
        ↓
generate realization

Conformal projection belongs to the placement stage.

The computational structure already exists.

The geometry already exists.

Projection establishes their correspondence.

That means projection is not simply a mathematical utility.

It becomes part of the language’s larger process of turning descriptions into realizable systems.


Toward Physical Cellular Systems

At this point the progression of Part V becomes clear.

We began with geometry:

Spatial Domain
      ↓
Vertices
      ↓
Edges
      ↓
Loops
      ↓
Faces
      ↓
Meshes

We then introduced computation into that space:

Meshes
      ↓
Cell Placement
      ↓
Local Rules
      ↓
Emergent Behavior

Conformal projection now connects that computation back to arbitrary geometry:

             GEOMETRY
                 |
                 v
             CELLULAR
            COMPUTATION
                 |
                 v
             PROJECTION
                 |
                 v
          PHYSICAL SURFACE

The result is a much larger idea than either geometry or cellular automata alone.

MatterScript can describe computation that has a place.

And once computation has a place, it can have:

  • distance,
  • adjacency,
  • orientation,
  • propagation,
  • delay,
  • physical constraints,
  • and eventually measurable behavior.

Why Conformal Projection Matters to MatterScript

Conformal projection is the point where spatial computation begins to become physical computation.

Cell placement established that locations can be computational entities.

NKS established that simple local rules can produce complex global behavior.

Geometry established that space can be explicitly constructed and represented.

Projection connects these ideas.

It provides a mechanism for taking a computational structure and embedding it into a physical shape while preserving the local relationships that define the computation.

The resulting model is neither purely geometric nor purely algorithmic.

It is both.

              MATTERSCRIPT

        computation + geometry
                  |
                  v
             relationships
                  |
                  v
               locality
                  |
                  v
              placement
                  |
                  v
             physical form

The deeper principle is:

Computation does not have to happen inside an abstract machine. The machine itself can be a geometry.

Once that is possible, space is no longer merely the environment in which computation runs.

Space becomes part of the computation.


Looking Ahead: Delay Synthesis

Conformal projection gives cells a physical location.

The next question is unavoidable:

What does physical distance mean computationally?

If two computational elements are separated by a measurable distance, a physical implementation may require a signal to travel between them.

That travel can take time.

A path through geometry can therefore become a timing relationship:

        A
        |
        | physical path
        |
        +------------------+
                           |
                           v
                           B

        distance → propagation → delay

This suggests a new computational primitive:

A spatial relationship can become a temporal relationship.

That is the subject of the next chapter.

Delay Synthesis explores how geometry, propagation, and timing can be brought together so that the physical structure of a system contributes directly to its computational behavior.

The progression continues:

space
  ↓
placement
  ↓
geometry
  ↓
distance
  ↓
propagation
  ↓
delay
  ↓
space-time computation

MatterScript has constructed the space.

Now it is time to make that space compute in time.

Drafting in progress…

Delay Synthesis

Drafting in progress…

Sensor Placement

Drafting in progress…

Hardware Fidelity

Drafting in progress…

Part V: Building Physical Systems

Introduction

MatterScript is unique because programs describe physical systems rather than abstract algorithms.

This section demonstrates how to construct simulations directly from engineering knowledge instead of numerical approximation.

Drafting in progress…

Cellular Automata

Drafting in progress…

Heat Flow

Drafting in progress…

Fluid Flow

Drafting in progress…

Elastic Materials

Drafting in progress…

Electromagnetic Fields

Drafting in progress…

Biological Systems

Drafting in progress…

Multi-Physics Coupling

Drafting in progress…

Part VII: Programming Space-Time

Programming Space-Time

Introduction

This is the conceptual heart of the book.

Most languages program only state.

MatterScript programs spacetime.

Readers learn how distance, propagation, causality, locality, synchronization, and computation become one unified programming model.

Drafting in progress…

Propagation Instead of Execution

Drafting in progress…

Space-Time Coordinates

Drafting in progress…

Local Causality

Drafting in progress…

Global Emergence

Drafting in progress…

Deterministic Physical Computing

Drafting in progress…

Software Meets Semiconductor Physics

Drafting in progress…

Part VIII: Compilers

Compilers

Introduction

MatterScript’s compiler is unusual because it performs two different jobs simultaneously.

It translates programs into executable logic while also solving a geometric placement problem.

Readers will see every stage of the compiler pipeline, from parser through placement and VHDL generation.

Drafting in progress…

Parsing

Compiler Directives

Up to this point, every construct we’ve introduced has described the computation itself. Definitions, places, invocations, and transform rules all become part of the association graph that ultimately realizes the computation.

Sometimes, however, we need to communicate with the compiler rather than with the computation.

MatterScript uses compiler directives for this purpose. Directives begin with the @ character and provide information about how a program should be interpreted, analyzed, or realized. Unlike definitions or invocations, directives do not become part of the computational network. They exist only during compilation.

For example, a program may request a particular backend:

@target(posix, path="/dev/ttyS0", priority=100)

This directive tells the compiler that the resulting program should be realized using the POSIX backend and provides backend-specific configuration options. Another backend might instead target an FPGA, a GPU, or another execution environment without requiring any changes to the computational description itself.

Directives also establish the domain in which a program operates.

@domain(spatial3d)

A domain extends the language with concepts appropriate to a particular problem space. The spatial3d domain, introduced elsewhere in this book, provides geometric primitives and operations for constructing three-dimensional models while remaining fully integrated with MatterScript’s computational model.

Because domains are selected explicitly, the core language remains intentionally small. New domains can introduce specialized vocabularies for geometry, signal processing, chemistry, biology, or other disciplines without complicating the language that every programmer must learn.

Compiler directives therefore separate the description of the computation from the configuration of the toolchain. The computation remains portable and backend-independent, while the directives provide the compiler with the context needed to realize that computation efficiently on a particular platform or within a particular domain.

Drafting in progress…

Abstract Syntax Trees

Every MatterScript program begins life as text.

FULLADD($A $B $C)(SUM<> CARRY<>)

To a human, this is a readable description of a computation.

To a compiler, it is only a sequence of characters.

Before the compiler can reason about the program, it must recover its structure.

That structure is represented internally as an Abstract Syntax Tree, or AST.


The Program Beneath the Text

An AST ignores formatting and punctuation that exist only to help humans read the language.

For example,

AND($A $B)(RESULT<>)

might be represented internally as something conceptually similar to

Invocation
 ├── Name: AND
 ├── Destination List
 │     ├── $A
 │     └── $B
 └── Source List
       └── RESULT<>

This tree captures the meaning of the program without preserving its exact textual representation.

Whitespace disappears.

Comments disappear.

Formatting disappears.

Only structure remains.


The AST Is Not the Computation

One of the easiest mistakes to make when thinking about compilers is to imagine that the AST somehow is the program.

It isn’t.

The AST is simply the compiler’s understanding of the source text.

It exists only long enough for the compiler to analyze the language.

The computational model appears later.

Source Code
        │
        ▼
Abstract Syntax Tree
        │
        ▼
Association Graph
        │
        ▼
Backend Realization

This distinction is important.

The AST understands syntax.

The association graph understands computation.


Compiler AST

The abstract syntax tree (AST) for IPL/NPL dialects translates high-level component declarations, resolution blocks, and transition rules into structured intermediate representations.

Understanding how source text maps to internal Go/Zig structs (network.Definition, Arg, and Place) is essential for building custom evaluators, validators, and code generators.


Shorthand Truth-Table Syntax

To streamline truth-table definitions and value transform rules, the parser supports a shorthand row notation. Instead of requiring a full nested component signature, shorthand rows specify input identifiers separated by commas, followed by bracketed output targets.

Example: Binary Equality Evaluation

binaryequal[(a<> b<>) 
   <$a$b()>
    : 0,0[TRUE]
      0,1[FALSE]
      1,0[FALSE]
      1,1[TRUE]  
]


Internal AST Mapping

When the compiler encounters a shorthand truth-table row inside a contained section, it synthesizes a nested Definition object representing that transition row.

The table below illustrates how the components of a shorthand row map to internal network.Definition fields:

Source Text ElementAST Field (network.Definition)Internal Representation
Input Tuple (0,0).nameCaptured as the row’s name string (e.g., "0,0").
Bracketed Target ([TRUE]).resolutionA slice of Statement unions holding SourceFill structs (.fill), where dest_name is empty or implicit and expr captures the target value ("TRUE").
Sub-Components.containedEmpty (&.{}) for leaf truth-table rows, or nested Definition slices for hierarchical logic blocks.

Structural Breakdown

For the binaryequal example above, the resulting parent definition holds four contained shorthand definitions within its AST:

#![allow(unused)]
fn main() {
Definition{
    .name = "binaryequal",
    .sources = &.{
        Arg{ .kind = .place, .name = "a" },
        Arg{ .kind = .place, .name = "b" },
    },
    .destinations = &.{},
    .resolution = &.{},
    .contained = &.{
        Definition{
            .name = "0,0",
            .resolution = &.{
                Statement{ .fill = SourceFill{ .dest_name = "", .expr = "TRUE" } },
            },
        },
        Definition{
            .name = "0,1",
            .resolution = &.{
                Statement{ .fill = SourceFill{ .dest_name = "", .expr = "FALSE" } },
            },
        },
        Definition{
            .name = "1,0",
            .resolution = &.{
                Statement{ .fill = SourceFill{ .dest_name = "", .expr = "FALSE" } },
            },
        },
        Definition{
            .name = "1,1",
            .resolution = &.{
                Statement{ .fill = SourceFill{ .dest_name = "", .expr = "TRUE" } },
            },
        },
    },
}

}

Parsing MatterScript Expressions: AST Generation Guide

This section details how MatterScript parses complex component definitions—such as gate-level logic or truth-table lookups—into the Abstract Syntax Tree (AST).


Anatomy of the Expression

Consider the following MatterScript snippet representing a full adder definition with explicit truth-table mappings (TAG-184):

  // Linear: TAG-184 Pure Value Place of Resolution
      
      FULLADD($A,$B,$C)(<> CARRYOUT<>)
        
      FULLADD[(A<> B<> CI<> )($SUM $CO)
        
        $A$B$CI :
        
        S,U,W[SUM<S> CO<W>] 
        S,U,X[SUM<T> CO<W>]
        S,V,W[SUM<T> CO<W>] 
        S,V,X[SUM<S> CO<X>]
        T,U,W[SUM<T> CO<W>] 
        T,U,X[SUM<S> CO<X>]
        T,V,W[SUM<S> CO<X>] 
        T,V,X[SUM<T> CO<X>] 
      ]

When the parser encounters this structure, it translates the linear syntax into a hierarchical AST node structure.


Step-by-Step AST Translation

  • Line Comment & Metadata: The leading \\// acts as a metadata comment, attaching tags (e.g., TAG-184) to the enclosing expression node for diagnostic and resolution tracking.
  • Gate / Component Invocation: FULLADD($A,$B,$C)(<> CARRYOUT<>) is parsed into an invocation node containing positional input arguments ($A, $B,$C) and output resolution placeholders (CARRYOUT<>).
  • Truth Table Mapping ([...]): The bracketed block defines a truth-table or state-transition matrix. The parser maps input tuples ($A, $B,$CI) to output bindings ($SUM,$CO).
  • Resolution Rules: Each line (e.g., S,U,W[SUM<S> CO<W>]) is parsed into individual evaluation clauses that feed directly into the SourceFill struct’s parsed_expr field.

Integration with SourceFill

In the compiler backend, when an unresolved invocation is detected within a fill statement, the raw string is parsed and stored using the following structure:

#![allow(unused)]
fn main() {
pub const SourceFill = struct {
    /// The destination place being filled (output of definition)
    dest_name: []const u8,
    /// The expression whose value fills the place
    expr: []const u8,
    /// Parsed expression when the fill contains an unresolved invocation.
    parsed_expr: ?*const Expr = null,
};

}

Note: Ensure that any custom parser extensions handling transformation rules preserve the table row ordering, as downstream code generators rely on sequential pattern matching for hardware layout synthesis.


This unified AST representation allows evaluators to process truth-table rows uniformly alongside standard nested hardware blocks while eliminating boilerplate component naming in dense logic tables.


A Stable Foundation

Almost every later stage of the compiler builds upon the AST.

Semantic analysis verifies that names are valid.

Definitions are connected to invocations.

Domains introduce additional syntax.

Compiler directives are interpreted.

Geometry expressions are recognized.

Errors are reported against the original source.

Because every feature is represented in a common tree structure, the compiler can grow without constantly rewriting its earlier stages.


Trees Give Way to Graphs

MatterScript begins as a tree because people write text one line at a time.

But computation is not a tree.

It is a graph.

One invocation may feed many others.

Names connect distant parts of a program.

Definitions reference other definitions.

Causal relationships span the entire design.

The compiler therefore performs a fundamental transformation.

It converts the tree describing the syntax into a graph describing the computation.

This is where MatterScript begins to diverge from conventional programming languages. In many compilers, the AST remains the central representation throughout optimization and code generation. In MatterScript, it is only a stepping stone. Once the association graph has been constructed, the compiler has recovered the true structure of the program, and from that point onward every optimization, transformation, and backend operates on relationships rather than on source code.

In later chapters, when we introduce the spatial3d domain, we will discover that geometry is parsed into the same abstract syntax tree as every other MatterScript construct. The compiler makes no fundamental distinction between a logic gate, a chemical transform rule, or a geometric primitive. They are all simply structured descriptions waiting to be transformed into a common association graph.

Drafting in progress…

Runtime Execution Models

Every definition you write describes places, and rules for filling them.

But not every place behaves the same way once it’s filled.

Consider two examples, side by side:

AND2[(A<> B<>)($OUT)
  $A $B :
  p,r[Z0]
  ...
]
REACTION[(H2<> O2<>)($WATER)
  @runtime(consumable)
  $H2 $O2 :
  ha,hb,oa[RXN]
  RXN[WATER<w1>]
  RXN[WATER<w2>]
]

Both use the same joint-match syntax. Both describe a rule that fires once its inputs are present. But they mean something different by “present.”


The default: a signal, once true, stays true

In AND2, once the place p holds a value, it holds that value for good. Nothing consumes it. Another rule can read p a hundred times without using it up — that’s exactly how a wire behaves. Once asserted, a signal doesn’t disappear because something downstream looked at it.

This is the model every definition gets automatically, with no annotation required. It’s the right model for anything that behaves like ordinary digital logic: a value is either present or it isn’t, and being read doesn’t change that.

@runtime(consumable): a token, once used, is gone

REACTION is doing something different. ha, hb, and oa aren’t signals sitting on wires — they’re meant to behave like reactant molecules. Once they combine to form RXN, they’re spent. They can’t also go on to power a second, unrelated reaction. If you fed the reactor two hydrogen molecules and one oxygen molecule, you’d expect exactly one water-forming event — not an ever-present “hydrogen is available” signal that keeps triggering new reactions forever.

@runtime(consumable), written once in a definition’s header, tells MatterScript that this definition’s places work that way: matching a rule removes the tokens that satisfied it. Feed in a second ha, hb, oa set later, and the rule can fire again — because presence, this time, really did mean “this specific instance is here right now,” not “this condition has become permanently true.”

Where to put it

The directive goes in the same header position as @domain and @generate — after the source and destination lists, before the resolution’s ::

REACTION[(H2<> O2<>)($WATER)
  @runtime(consumable)
  $H2 $O2 :
  ...
]

Leaving it out is not an oversight to fix — it’s the correct choice for the overwhelming majority of definitions, since ordinary digital logic is the default for a reason: it’s what nearly everything you’ll write actually is.

What actually changes underneath

You don’t need to think about this to use the directive, but it helps to know what’s different, the same way it helps to know an AST exists even if you never inspect one by hand.

An ordinary definition’s places are checked once and, once satisfied, stay satisfied — a rule’s inputs being true is a permanent fact about that run. A consumable definition’s places are tracked as a multiset: a running count of how many of each named token are currently available. Firing a rule decrements the count for everything it consumed and increments the count for whatever it produces. A rule that needed three tokens and produced two behaves exactly like a chemical equation balancing quantities, not a gate holding a state.

This also means a consumable definition can produce more than one output from a single firing — RXN[WATER<w1>] and RXN[WATER<w2>] both fire off the same reaction event, and both counts get recorded. An ordinary definition has no equivalent notion of “this place fired twice” — a place either became valid or it didn’t.

What you don’t need to design yourself

You don’t need to write two versions of your definition, or handle the bookkeeping of what’s been consumed. The runtime reads the tag once and picks the correct execution model automatically — you write the rules the same way either way, using the same joint-match and fill syntax throughout this book. The tag is the only thing that changes.

If a testbench or tool feeds a consumable definition input meant for the ordinary model (or vice versa), that mismatch is caught and reported plainly rather than silently producing a meaningless result — you’ll know immediately if a definition and the way it’s being driven have gotten out of sync.

When to reach for it

Ask whether your places represent conditions or quantities.

  • If a place being present means “this fact is now true, and stays true” — ordinary digital logic, dual-rail encoding, anything you’d eventually expect to become a real circuit — leave the definition untagged.
  • If a place being present means “exactly this many instances of this thing currently exist, and using one changes how many are left” — reactants, resources, anything with a notion of depletion or multiplicity — reach for @runtime(consumable).

There’s no hardware realization for the consumable model yet — no VHDL backend understands token depletion the way it understands a persistent signal. For now, @runtime(consumable) definitions run only against MatterScript’s reference runtime, as a way of expressing and testing ideas that don’t map onto a wire at all. That may change; it hasn’t yet, and the directive doesn’t pretend otherwise.

Verification Process

Verification Process

Every example under examples/docs/{issue-id}/ is checked automatically by verify.sh, which pulls its list of examples and their expected status from docs/generated/linear/status.json, which is built by the weave utility. Each example advances through up to four independent stages. A stage that finds nothing to do reports - rather than pass or fail, and a failure at one stage does not block the stages after it from being attempted where possible, so a single run tells you as much as it can about where an example stands.

The four stages

1. Parse. The .ms.ipl source is parsed and validated. This is the only stage every example must pass; everything downstream depends on having a valid network.

2. Runtime. If a sibling <stem>.tb.vec file is present next to the .ms.ipl source, the design is run through the MatterScript runtime directly — no VHDL, no external simulator. The vector file declares input and output columns and a table of rows; each row is driven into the parsed network as one wavefront, and the resulting output values are checked against what the row expects. A design with no .tb.vec file shows - in this column: parsing correctly is still meaningful on its own, but nothing has verified its behavior.

Cells in a vector row are raw tokens: the token driven on an input place, or the token expected on an output place. There is no value↔symbol mapping layer — if the design uses p/q for a dual-rail wire, the vectors use p/q, and it is the DUT author’s convention that those tokens mean “0” and “1”. This keeps the testbench pure: it exercises the expression in the vocabulary the expression already speaks, and the meaning of a token is decided by whoever consumes the output.

A minimal vector file for AND2 looks like this:

@dut(AND2)
@vectors(A, B : OUT)
@mode(wavefront)

p, r : Z0
p, s : Z0
q, r : Z0
q, s : Z1

@dut names the definition under test (optional — it defaults to the first definition in the network). @vectors declares the input and output columns and must precede the rows. Each subsequent line is one row, one wavefront. A - in an output cell means “don’t check this output”. A row can assert !stall, meaning the wavefront is expected to never complete — the correct behavior for an input combination the design deliberately leaves unresolved.

Streaming a sequence of tokens

A single-row-per-wavefront testbench is fine for a combinational design, but a state machine wants to be fed a sequence: one bit at a time, with the design’s state carried from each wavefront into the next. That is what @stream provides.

The problem it solves is this. A state machine like a parity detector has a state (E or O) and an input bit (0 or 1), and on each wavefront it consumes one bit and produces the next state. To feed it a bit stream 1, 1, 0, 1, you need the first row to seed the state explicitly, and every row after that to take its state input from whatever the previous row’s next output was. Writing that by hand means transcribing each intermediate state into the next row, which is exactly the kind of bookkeeping a testbench should do for you.

@stream plus @carry does that bookkeeping. @carry(input=output) declares that the named input column is fed from the named output column of the previous row. A cell written as _ in that input column means “use the carried value here” rather than a literal token. The first row still needs a literal seed for any carried column, because there is no previous row to pull from.

A parity detector and its stream vectors:

PARITY[(state<> bit<>)($next)
  $state$bit() :
  E,0[next<E>]
  E,1[next<O>]
  O,0[next<O>]
  O,1[next<E>]
]
@dut(PARITY)
@vectors(state,bit : next)
@carry(state=next)
@stream()

E,1 : O
_,1 : E
_,0 : E
_,1 : O

Read row by row:

  • Row 1 seeds state=E and drives bit=1. The table says E,1 produces next=O, and the row expects O. Row 1’s actual output becomes the carried value for the next row.
  • Row 2 writes _ for state, meaning “take it from @carry(state=next)” — that is, from row 1’s next, which was O. So state=O, bit=1, which produces next=E. The row expects E.
  • Row 3 carries E forward, drives bit=0, produces next=E.
  • Row 4 carries E forward, drives bit=1, produces next=O.

The sequence of bits tested is 1, 1, 0, 1, and the state moves along the stream without the vector author ever writing an intermediate state name. This is what “a state-dependent read/test head moving along the stream” means: the head is the pair (state, bit) being read from the stream at each step, and @carry keeps the state half of that head synchronized with where the previous step left it.

Two parse-time checks make a silently-broken carry loud rather than silent, which matters because a carry that fails to thread is otherwise indistinguishable from a correct run until the rows after it start mismatching:

  • A _ in a column with no matching @carry(input=…) is rejected at parse time — there is nothing to pull from, so the row could never run.
  • A @carry whose target is not an output column of the DUT, or whose source is not an input column, is rejected before any row runs, so a typo in the mapping is caught as a testbench error rather than as a mysterious mid-stream stall.

The runtime then carries the actual output of each completed wavefront forward, not the expected value, so a row whose output is - (don’t-check) still threads its real result into the next row. !stall is not supported under @stream today, because a stalled wavefront leaves the carry state undefined.

3. GHDL (VHDL emission and analysis). The parsed network is emitted as VHDL and run through GHDL’s analyzer. This checks that the design translates to synthesizable VHDL and that GHDL accepts it, independent of what the design actually computes.

4. Simulate. If simulation is requested for the issue, the emitted VHDL is run through a GHDL simulation and its output is checked. This is the closest stage to a full hardware-equivalent run, at the cost of being the most indirect way to catch a bug in the network itself — a failure here can originate in the network, the emitter, or the simulation harness.

Reading a result

An example’s overall result column reflects whether every stage that was expected to pass, did. Expectations come from Linear’s tracked status (Implemented, Partial, Spec Only, and so on) crossed with whether simulation was requested:

  • ✅ PASS — parse, runtime (if present), GHDL, and simulation (if requested) all succeeded, matching an Implemented expectation.
  • ❌ FAIL — a stage that was expected to pass, didn’t.
  • ⬛ expected fail — a stage failed, but that failure was itself the expected outcome (for example, an issue tracked as Partial whose known-incomplete feature causes GHDL to reject it).
  • 🟡 unexpected pass — everything passed, but Linear’s tracked status didn’t expect it to (commonly a Partial or Spec Only issue whose example happens to already work). This isn’t a failure; it’s a signal that the tracked status may be stale and worth revisiting.

Current results

Issue    example                          expected     parse runtime    ghdl  simulate   result
-------------------------------------------------------------------------------------------------
TAG-112  mobius_ring.ms.ipl               Partial      ok    -          FAIL  -          ⬛ expected fail
TAG-129  example-12.19.ms.ipl             Implemented  ok    ok 8/8     FAIL  FAIL       ❌ FAIL
TAG-130  example-12.1.ms.ipl              Implemented  ok    ok 8/8     ok    FAIL       ❌ FAIL
TAG-136  example-12.45.ms.ipl             Implemented  ok    ok 14/14   ok    -          ✅ PASS
TAG-137  example-12.41.ms.ipl             Implemented  ok    ok 4/4     ok    -          ✅ PASS
TAG-138  example-12.40.ms.ipl             Implemented  FAIL  -          -     -          ❌ FAIL
TAG-142  example-12.3.ms.ipl              Partial      ok    -          ok    -          🟡 unexpected pass
TAG-143  example-12.2.ms.ipl              Implemented  ok    ok 4/4     ok    -          ✅ PASS
TAG-147  example-12.13.ms.ipl             Implemented  ok    -          ok    -          ✅ PASS
TAG-148  example-12.11.ms.ipl             Implemented  ok    ok 8/8     ok    FAIL       ❌ FAIL
TAG-149  example-12.9.ms.ipl              Spec Only    ok    -          FAIL  -          ⬛ expected fail
TAG-151  example-12.17.ms.ipl             Partial      ok    -          ok    -          🟡 unexpected pass
TAG-153  example-12.8.ms.ipl              Implemented  ok    -          ok    -          ✅ PASS
TAG-160  example-12.33.ms.ipl             Implemented  ok    -          ok    -          ✅ PASS
TAG-163  example-12.36.ms.ipl             Implemented  ok    -          ok    -          ✅ PASS
TAG-177  example-12.26.ms.ipl             Implemented  ok    ok 8/8     ok    -          ✅ PASS
TAG-180  example-12.10.ms.ipl             Implemented  ok    ok 20/20   FAIL  -          ❌ FAIL
TAG-181  example-12.11.ms.ipl             Implemented  ok    ok 4/4     ok    -          ✅ PASS
TAG-184  example-12.24.ms.ipl             Implemented  ok    ok 8/8     ok    -          ✅ PASS
TAG-185  case-unique-signals.ms.ipl       Implemented  ok    -          ok    -          ✅ PASS
TAG-187  generate_syntax.ms.ipl           Implemented  ok    -          ok    -          ✅ PASS
TAG-189  example-12.14-arbitration.ms.ipl Implemented  ok    -          ok    -          ✅ PASS
TAG-190  example-12.5.ms.ipl              Implemented  ok    -          ok    -          ✅ PASS
TAG-192  entity_labeling.ms.ipl           Partial      ok    -          ok    -          🟡 unexpected pass
TAG-196  cube.ms.ipl                      Implemented  ok    -          FAIL  -          ❌ FAIL
TAG-198  example-12.25.ms.ipl             Implemented  ok    ok 8/8     ok    -          ✅ PASS
TAG-200  dual-rail.ms.ipl                 Partial      ok    ok 4/4     ok    -          🟡 unexpected pass
TAG-201  combustion-reaction.ms.ipl       Partial      ok    -          ok    -          🟡 unexpected pass
TAG-207  example-12.35.ms.ipl             Implemented  ok    -          ok    -          ✅ PASS
TAG-210  rule30.ms.ipl                    -            ok    -          ok    -          🟡 unexpected pass
TAG-211  example-12.18-bundling.ms.ipl    Partial      ok    -          FAIL  -          ⬛ expected fail
TAG-212  and3.ms.ipl                      Implemented  ok    ok 8/8     ok    -          ✅ PASS
TAG-217  parity.ms.ipl                    Implemented  ok    ok 4/4     ok    -          ✅ PASS
-------------------------------------------------------------------------------------------------
21 ok, 12 unexpected

The runtime stage now exercises seven examples: TAG-143, TAG-177, TAG-136, TAG-148, TAG-180, TAG-198, TAG-200, TAG-212, and TAG-217 — covering combinational logic, a full adder, a code detector, a dual-rail network, an AND3, and the parity stream test that motivated @stream. Every Implemented example that has a .tb.vec currently passes its runtime stage. Writing a .tb.vec for more of the Implemented examples — and especially for the ❌ FAIL rows where the runtime stage is still - — is the most direct way to find out whether a failure is in the network itself or in VHDL emission/simulation, since the runtime stage checks the design’s behavior without going through either.


Neighborhood Rules

Earlier in the book we introduced Transform Rules as one of the fundamental building blocks of MatterScript. A Transform Rule describes how one arrangement of tokens transforms into another. Small examples, such as Boolean logic gates, can be written by hand.

0,0[0]
0,1[1]
1,0[1]
1,1[1]

For simple definitions, this is entirely practical.

Real systems, however, rarely consist of four rules.

A cellular simulation containing millions of cells—or a three-dimensional material spanning billions of interacting regions—would require an enormous number of nearly identical transformation rules. Writing those rules by hand would be impossible.

MatterScript therefore provides the @generate directive, allowing large collections of neighborhood rules to be created automatically during compilation.


Programs That Generate Behavior

The @generate directive allows the programmer to describe an entire family of transformation rules using a compact specification.

@generate {
    ...
}

Rather than writing thousands or millions of individual rules by hand, the programmer describes the pattern from which those rules are constructed. During compilation, the generator expands into a complete transformation table before the association graph is built.

This idea is similar in spirit to the generate construct in VHDL, where a small fragment of source expands into large amounts of hardware. The difference is that MatterScript is not merely replicating circuitry—it is generating local computational behavior.


Neighborhood Rules

A Neighborhood Rule describes how a local region of a computational fabric evolves. For example, let’s define three rules:

// Bind to a 2D spatial domain with bounds
@domain(spatial2d, size: [300, 500])

@generate {
  // Format: [ Left , Center , Right ] -> New Center State
  [2, 1, 5]:4
  [3, 2, 4]:6
  [4, 4, 6]:4
}

The first rule [2, 1, 5]:4 can be interpreted as:

The cell under evaluation is currently in state 1.

Its local neighborhood matches the pattern [2, 1, 5] (left neighbor is 2, right neighbor is 5).

The rule transforms the center cell into state 4, leaving the local neighborhood in state [2, 4, 5].

Similarly, for the second rule [3, 2, 4]:6, if the cell under evaluation is in state 2, its left neighbor is in state 3, and its right neighbor is in state 4, the rule triggers and updates the center cell to state 6 (resulting in neighborhood state [3, 6, 4]).

The third rule [4, 4, 6]:4 introduces a pattern for state 4: if the center cell is 4, its left neighbor is 4, and its right neighbor is 6, the cell remains in state 4,resulting in neighborhood state [4, 4, 6].

With explicit domain declarations like @domain(spatial2d, size: [300, 500]), you decouple topology from extents. This allows the same neighborhood rules to compile across different grid sizes or dimensions without modifying the rule block syntax.

When there are multiple @domain contexts in a definition, other statements can sit between them, with the domain context persisting for the rest of the definition, or until a new @domain appears. @domain(…) acts by setting ambient context for everything after it in that resolution area.

Any number of transformations can be defined, and they are not restricted to numeric states. Numbers hold no arithmetic meaning in MatterScript; here is an example defining transformations using domain-specific tokens:

@domain(spatial2d, size: [300, 500])

@generate {
  // Format: [ Left , Center , Right ] -> New Center State
  [DE, AK, JD]:JC
}

In MatterScript, Neighborhood Rules use sparse notation: you only declare explicit state transitions, and any unlisted neighborhood pattern automatically defaults to Identity (the center cell keeps its current state). You can also use the wildcard * to match any token or state in a given position.

@domain(spatial2d, size: [300, 500])

@generate {
  // Format: [ Left , Center , Right ] -> New Center State
  
  // Wildcard: Any left state, center state 1, right state 5 -> transition to 4
  [*, 1, 5]: 4
  
  // Explicit rule
  [3, 2, 4]: 6
  
  // Unmentioned permutations (e.g., [0, 0, 0]) automatically retain their center state.
}

Nothing in this description refers to equations, variables, or arithmetic.

Instead, the rule simply asks:

Does this neighborhood exist?

If the answer is yes, the transformation occurs.

Each rule is therefore nothing more than a description of one possible local interaction. Collectively, the rules define how an entire computational fabric evolves over time.

All of the rules are then composed into a digital circuit that embodies all those transformations, in such a way that every cell in the generated field will exhibit identical behavior in accordance with that rule set. The field is now ready for state to start flowing into it, and all the cells, being independent digital circuits, react as soon as they receive their state and their neighbors are presenting their own state. The wiring between the cells extends as far as neccesary to allow a cell to read the state of all its neighbors, in this case the cell to the left and the cell to the right, in a 2D cellular automaton.


Computation as Local Transformation

Readers familiar with A New Kind of Science will recognize this immediately.

Instead of modeling reality by solving increasingly complex mathematical equations, we describe reality through collections of local transformation rules. Every cell interacts only with its immediate neighborhood, and the repeated application of these simple rules gives rise to remarkably rich global behavior.

MatterScript adopts this philosophy directly.

Neighborhood Rules are not lookup tables in the traditional programming sense. They are behavioral descriptions. Each entry specifies one possible local interaction, and together they describe the evolution of an entire computational system.

The compiler does not interpret these rules mathematically.

It compiles them into the association graph that realizes the specified behavior.


From One Rule to Billions

The real power of @generate becomes apparent when these local rules are replicated across space.

A two-dimensional cellular automaton containing one hundred million cells does not require one hundred million hand-written definitions.

The programmer defines a single local neighborhood and specifies how that neighborhood should be replicated.

The compiler manufactures the remaining computational fabric automatically.

Each generated cell possesses the same local behavior while remaining connected to its own unique neighbors.

The same principle naturally extends into three dimensions. Voxel simulations, reaction-diffusion systems, biological tissues, crystal growth, fluid dynamics, and many other spatial systems can all be expressed as collections of Neighborhood Rules distributed throughout a geometric structure.

The programmer describes the behavior.

The compiler constructs the fabric.


Scaling Without Complexity

One of the central ideas of MatterScript is that source code should describe patterns, not instances.

The amount of source code should not grow simply because the problem becomes larger.

Instead, the programmer captures the behavior once, and the compiler expands that behavior into whatever scale is required.

Neighborhood Rule
        │
        ▼
@generate
        │
        ▼
Millions or Billions
of Generated Rules
        │
        ▼
Association Graph
        │
        ▼
Target Backend

This separation of concerns keeps programs remarkably compact. The programmer reasons about local interactions, while the compiler is responsible for constructing the enormous computational structures needed to realize them.


Toward Computational Matter

Neighborhood Rules are where MatterScript begins to move beyond conventional software.

Traditional programs execute instructions over collections of data.

MatterScript constructs collections of interacting regions, each governed by the same simple local behavior.

As we introduce domains such as @domain(spatial3d), these neighborhoods no longer need to represent cells in a one-dimensional automaton. They can represent adjacent voxels, neighboring finite elements, atoms within a crystal lattice, interacting molecules, or regions within a simulated biological tissue.

The same mechanism applies regardless of scale.

A local interaction remains local.

The compiler simply extends that interaction across whatever geometry the programmer has described.

In this way, MatterScript unifies Karl Fant’s model of causal association with the rule-based evolution of A New Kind of Science. The programmer defines what interactions are possible. The compiler constructs the computational fabric in which those interactions unfold. From a handful of simple Neighborhood Rules, systems containing millions or even billions of autonomous interacting regions can be synthesized automatically, all while preserving the same elegant, local description from which they began.

Drafting in progress…

Placement Algorithms

Drafting in progress…

Delay Insertion

Drafting in progress…

Symbols and Encoding

By this point in the pipeline, the design has been parsed, its transitions analyzed, its places placed, and its delays inserted. What remains is to turn it into something a physical medium can carry — bits on a wire, gates in silicon, memory cells on a chip. That step is where IPL’s symbolic vocabulary has to be reconciled with the fact that hardware doesn’t know what a symbol means.

This chapter is about that reconciliation. It’s not a chapter about any particular backend; it’s about the conceptual bridge every backend has to cross. Once the bridge is understood, the VHDL emitter, the WGSL emitter, the C generator, and the FPGA synthesizer all become readable as variations on one theme.

The problem

IPL programs are written in tokens. A transition rule might be:

S,U,W[K,M]

S, U, W, K, and M are all symbols. They have no numeric value. They don’t stand for bits. They’re names for tokens — a token being the unit of content that flows through a place, in the same sense that a number flows through a register or a packet flows through a wire.

So S,U,W[K,M] reads: when S, U, and W are simultaneously valid on their respective source places, assert K and M on their respective destinations. The symbols are the operands and results of a combinational relationship, and their names are the only thing that distinguishes them.

The hardware, though, wants bits. A wire carries a voltage. A memory cell holds a small integer. A gate takes a 0 or a 1. None of these substrates can carry the symbol K. They can only carry some encoding of it.

So there’s an implicit step between “the design is correct in terms of symbols” and “the design is correct in terms of bits”. That step is called encoding, and it is the subject of this chapter.

The straightforward approach: one token per wire

The simplest encoding is also the one that’s used everywhere in the current toolchain. Each place is represented by a single wire — a signal, in VHDL terms — and that wire carries exactly one token at a time. The wire has two parts:

  • a valid bit, saying whether the wire currently carries any token at all, and
  • a payload, a small integer that identifies which token it carries.

For a design with at most 128 distinct tokens, the payload is 7 bits and the whole wire is 8 bits. This is the ncl_signal type that the VHDL emitter produces throughout.

ncl_signal = { data[6:0], valid }

Every place in every definition maps to exactly one such wire. A definition with three source places and two destination places has three input wires and two output wires. The transition rule S,U,W[K,M] becomes a small combinational block that reads the three input wires, checks that each carries the expected token, and drives the two output wires with the appropriate tokens.

The two parts of the wire do different jobs:

  • The valid bit carries the NCL discipline. A wire with valid = 0 is empty; a wire with valid = 1 is ready. Completeness checks, synchronization, and the null/data handshake all operate on the valid bit alone. They don’t care what the payload says.

  • The payload carries the symbolic content. It’s the token’s identity, encoded as a small integer.

Splitting the wire this way is what lets the toolchain reason about NCL’s correctness properties (completeness, mutual exclusion, persistence) independently of what any particular token means. The valid bit is the part that’s about timing and synchronization; the payload is the part that’s about computation.

How tokens get their payload value

The mapping from tokens to payload integers is called interning. For each definition, the toolchain walks every place the definition mentions and every token that could appear on it, and assigns each a distinct integer, in first-seen order. So a definition whose first two tokens are K and L might intern them as K → 0, L → 1. A definition whose first tokens are S and T might intern them as S → 0, T → 1.

Interning is per definition, not global. This matters because two definitions can reuse the same token name — say both use S to mean something — and there’s no reason for them to agree on a numeric value. Each definition is its own scope; each has its own table.

Within a definition, though, the table is shared. Every place the definition mentions resolves tokens against the same table. If S appears as a fill in one rule and as a key in another, both occurrences go through the same interning step and produce the same integer. This is what makes equality of tokens meaningful: two tokens are the same iff their interned ids are the same.

The interning table is emitted alongside the design, usually as a comment. For a small definition it might look like:

-- symbol encoding: S=0, T=1, U=2, V=3, W=4, X=5

This comment is not decoration. It’s the specification of the encoding. If you read the emitted VHDL and see data_value(3), you can look up that 3 in the symbol encoding and know the token is V.

What an encoded rule looks like

Once tokens have payloads and places have wires, a transition rule becomes a small combinational expression over those wires. For the FULLADD definition’s rule

S,U,W[K,M]

the emitted logic is roughly:

K_valid  <= valid_of(S) and valid_of(U) and valid_of(W)
K_data   <= data_value(0)      -- K's interned id
M_valid  <= valid_of(S) and valid_of(U) and valid_of(W)
M_data   <= data_value(1)      -- M's interned id

In practice it’s wrapped in a process that also handles the dispatch-table structure of the definition, but the essential shape is what you see: the rule’s guard is a conjunction of valid bits on the source places, and the rule’s result is the interned id of the token being written, presented on the destination’s payload.

This is what makes the encoding work. S, U, W, K, M become small integers, and those integers travel as payloads on the wires between places. Nothing downstream needs to remember that K was originally a symbol; it just needs to know that the wire carries integer 0.

What the encoding preserves, and what it loses

Encoding preserves identity. Two places that ought to be carrying the same token will carry the same integer. Two places that ought to be carrying different tokens will carry different integers. Equality of integers is the correct notion of equality of tokens.

Encoding loses meaning. The integer 0 doesn’t mean anything. It isn’t “false”, it isn’t “0”, it isn’t “the absence of something”. It’s the id of whatever token was interned first in this definition’s table. The same definition might use 0 for K in one compilation and for S in another, depending on the order the tokens happen to appear.

This is fine, because the design doesn’t depend on the meaning of any token. It only depends on tokens being distinguishable, and on rules firing when their guard tokens are present.

The consequence is that you cannot test a design by inspecting its encoded form alone. A signal carrying data_value(0) doesn’t tell you whether the design is computing 0 or K or any other symbol — it tells you the design interned some token as 0. To know which token, you consult the symbol encoding table. To know whether the design is correct, you run it and compare the emitted tokens against expected tokens, symbolically. This is what the testbench does: it drives symbolic tokens in, checks symbolic tokens out, and lets the encoding remain an internal detail.

Where the encoding boundary sits

The encoding is not applied all at once at the end of the pipeline. It is applied at the boundary between the symbolic world and the physical world, and that boundary is different for each backend:

  • For VHDL emission, the encoding happens at the moment of emission. The parser has already produced an AST full of symbols; the VHDL emitter walks that AST and translates each symbol into a wire value. The result is VHDL that speaks in data_value(N) and valid_of(X).

  • For the testbench runtime, the encoding happens at the moment a token is asserted on a place. The testbench receives a symbolic token from the vector file (say K), looks it up in the definition’s interning table, and stores the integer in the environment. When it reads an output back, it does the reverse lookup.

  • For FPGA synthesis, the encoding happens once, during the front-end translation from IPL to whatever intermediate representation the synthesis tool wants. Symbols become integers become signals become LUTs.

  • For WGSL emission, the encoding can be much lighter — a WGSL integer can hold a token id directly, so the “valid bit” and “payload” distinction collapses into a single integer where 0 might mean “empty” and everything else means “carrying that token”.

In every case, the shape of the encoding is the same: symbols become small integers; wires carry the integers; the mapping is local to each definition.

Optimizations and where they live

The straightforward encoding — 8-bit wires, one per place — is what the current toolchain uses everywhere. It is uniform, it is correct, and it is worth understanding as the baseline.

There are two optimizations that are natural extensions of it, and it’s useful to know where they’d fit, even though the toolchain doesn’t use them yet.

Binary narrowing. When a place’s vocabulary is known to be exactly {0, 1}, its payload can be dropped to a single bit, and the wire becomes 2 bits (one data + one valid) instead of 8. This saves area and power in silicon. It does not change behavior. It’s a purely local transformation — a definition whose only tokens are 0 and 1 can be narrowed definition-by-definition, and the surrounding design doesn’t need to know.

Bundling. When a logical value is inherently wider than one token — say, an 8-bit integer that’s meant to be a single value rather than eight independent booleans — the natural encoding is a bundle: several wires whose union carries the value, with the value split across them as bits. This is a different representational choice, not just an optimization. It changes the port list, the completeness rules, and the testbench syntax. It’s the right model when the design has places that are meant to carry multi-bit values as single units, and it’s not needed when every place carries a single symbolic token.

Both of these live after encoding, in the sense that the encoding step produces a value and the optimization step chooses how many wires carry that value. They are worth thinking about when you’re designing a backend, but they’re not part of the core translation from symbols to bits. The core translation is: one place, one wire, one interned id.

Why this is worth knowing as a programmer

If you write IPL, you’ll eventually read the emitted VHDL, or the emitted C, or a waveform dump from a simulation, and you’ll see data_value(3) or token 3 or a signal that should be carrying K but is carrying an integer you don’t recognize. This chapter is what lets you read those artifacts. The integer is K’s interned id. The comment at the top of the file tells you the mapping. The design doesn’t know or care that you once called it K.

More importantly, understanding the encoding explains a subtle property of the whole system: the design’s behavior is independent of its encoding. Two compilations of the same IPL that intern tokens in different orders produce VHDL with different integer constants, but the same behavior. Two backends that use different wire types produce different physical artifacts, but the same behavior. The encoding is an implementation detail of the translation from symbols to substrate, and the design’s meaning lives above it, in the symbols.

That’s the property that makes IPL portable. It’s what lets the same program run on VHDL, on WGSL, on POSIX, and on an FPGA, with the same results. And it’s what makes the compiler’s job well-defined: translate each symbol to its interning id, translate each place to its wire, and let the surrounding design do the rest.

VHDL Generation

Drafting in progress…

WGSL Generation

FPGA Synthesis

Drafting in progress…

C & POSIX Code Generation

While VHDL generation targets spatial execution on reconfigurable hardware, the C & POSIX Code Generation backend lowers Invocation Language AST nodes into portable, deterministic ANSI C/POSIX software constructs. This allows space-time graph definitions to execute on conventional host operating systems and embedded processors.

Architectural Mapping

The software backend translates spatial-temporal graph semantics into low-overhead POSIX primitives without modifying the core IPL syntax:

  • Resolutions → Threads / Tasks: Top-level resolutions map directly to POSIX threads (pthread_create) or task routines.
  • Streams & Places → File Descriptors & Pipes: Data paths between processing nodes are emitted as POSIX byte streams, non-blocking FIFOs (mkfifo), or Unix domain sockets.
  • Tokens & Signals → POSIX Synchronization: Token passage across graph edges lowers to POSIX semaphores (sem_t), mutexes, or poll/select readiness checks to enforce deterministic signal flow.
+------------------------+        +---------------------------+
|    IPL AST Node        |        |    C / POSIX Target       |
+------------------------+        +---------------------------+
|  Resolution Node       |  --->  |  void* task_thread(void*) |
|  Stream / Data Place   |  --->  |  int pipe_fd[2]           |
|  Token / Signal Edge   |  --->  |  sem_t token_sem          |
+------------------------+        +---------------------------+

Compilation Pipeline Steps

  1. AST lowering: Flatten resolution graphs into explicit execution frames and state transitions.
  2. Buffer Allocation: Bind data channels to fixed-size ring buffers or file descriptor handles to prevent runtime dynamic memory allocations (malloc).
  3. Thread Instantiation: Synthesize main entry loops and initialize POSIX synchronization primitives (pthread_mutex_init, sem_init).
  4. Target Emission: Output clean, dependency-free ANSI C code suitable for compilation via gcc, clang, or target cross-compilers.

Target Attributes & Configuration

Low-level OS parameters that fall outside core language logic are specified directly via target attributes attached to nodes:

$sensor_node() : [
    @target(posix, path="/dev/ttyS0", priority=100)
    dev< stream_in >
]

This ensures full portability across environments while allowing explicit control over OS resources, scheduling priorities, and file paths when compiling for standard Linux or embedded POSIX runtimes.

Drafting in progress…

Embedded POSIX Runtimes & Apache NuttX

While general POSIX targets rely on full Linux or BSD operating system abstractions, embedded microkernels enforce strict resource bounds and zero-dynamic-memory constraints. Apache NuttX serves as a primary embedded target for Invocation Language (IPL), offering standard POSIX API compliance with low memory footprints and real-time execution guarantees across microcontrollers and RISC-V/ARM architectures.

Architecture & System Layering

The NuttX backend lowers IPL graphs into bare-metal POSIX C modules compiled directly into NuttX task images or Flat/Protected mode ELF binaries.

+-------------------------------------------------------+
|                IPL Execution Graph                    |
+-------------------------------------------------------+
                           |
                           v
+-------------------------------------------------------+
|          C / POSIX AST Code Generation                |
+-------------------------------------------------------+
                           |
                           v
+-------------------------------------------------------+
|            NuttX RTOS Adaptor Layer                   |
|  - POSIX Pthreads (`pthread_create`, `pthread_yield`) |
|  - Message Queues (`mq_open`, `mq_send`, `mq_receive`)|
|  - Character Driver Endpoints (`/dev/ttyS0`, `/dev/spi`)|
+-------------------------------------------------------+
                           |
                           v
+-------------------------------------------------------+
|     Bare-Metal Hardware / Embedded Substrate          |
|    (ESP32, HiFive RISC-V, ARM Cortex-M, STM32)        |
+-------------------------------------------------------+

Semantics Mapping

NuttX provides standard POSIX primitives, allowing IPL graph structures to map directly to RTOS constructs without requiring heavy OS abstraction layers:

  • Resolutions → NuttX Tasks / Pthreads: Top-level space-time resolutions instantiate as lightweight pthread nodes configured with fixed thread stack sizes and static priority levels.
  • Streams → POSIX Message Queues (mqd_t) / FIFOs: Token streams between processing nodes lower to POSIX message queues (mq_send/mq_receive) or native NuttX driver endpoints (/dev/chanN), bypassing user/kernel context switches in flat-build environments.
  • Token Placement → Named Semaphores (sem_t): Deterministic token passage along graph edges maps directly to POSIX counting semaphores (sem_wait/sem_post), ensuring strict real-time ordering.

Memory & Resource Allocation Strategy

Unlike desktop POSIX runtimes, the NuttX code generator enforces deterministic resource bounds to meet embedded real-time requirements:

  1. Zero Dynamic Allocation (malloc-free): All message queue buffers, task stacks, and token state structs are statically allocated at compile time in .bss or specialized RAM regions.
  2. Fixed Task Priorities: Execution nodes declare priority levels through target attributes to prevent priority inversion under the NuttX FIFO scheduler.
  3. Hardware Device Binding: IPL input/output nodes interface directly with NuttX device paths (/dev/sensor0, /dev/can0) via standard open(), read(), write(), and ioctl() POSIX calls.

Example IPL Domain Attribute Binding

NuttX-specific execution attributes are declared directly on IPL nodes using standard target annotations:

$motor_control_loop() : [
    @target(nuttx, stack_bytes=4096, priority=200, sched="fifo")
    ctrl< stream_in >
    
    @target(posix, path="/dev/pwm0")
    actuator< stream_out >
]

Drafting in progress…

Verification

FPGA Synthesis

Drafting in progress…

Web GPU Compute

Targeting WGSL: AST-to-WGSL translation, handling memory layout and @workgroup_size.

Buffer & Pipeline Abstractions: Mapping state memory to storage buffers (@binding) and managing ping-pong passes.

Execution & Dispatch: Setting up compute pipelines, dispatching workgroups, and handling headless vs. visual execution.

Headless Data Readback: Staging buffers and asynchronous GPU-to-CPU state extraction.

Drafting in progress…

NuttX RTOS and Embedded POSIX

Drafting in progress…

Verification

Drafting in progress…

Part IX: World Modeling

Introduction

MatterScript is ultimately not about FPGAs.

It is about building executable models of reality.

Instead of creating increasingly larger neural networks that implicitly approximate the world, MatterScript encodes engineering knowledge into hardware-verifiable executable models developed collaboratively by engineers and AI systems.

Drafting in progress…

Engineering Knowledge as Software

Drafting in progress…

Building a Radiator

Drafting in progress…

From Radiator to Automobile

Drafting in progress…

From Automobile to City

Drafting in progress…

Human-AI Collaboration

Drafting in progress…

Executable World Models

Drafting in progress…

Part X: Distributed Space-Time Systems

Introduction

Once computation has explicit geometry and explicit time, it naturally extends beyond a single FPGA.

MatterScript can interoperate with deterministic temporal fabrics in which every computation shares a common notion of identity, time, and location. This complements architectures such as MKSTORM, which provide deterministic time-space addressing and synchronization across distributed systems.

Drafting in progress…

Distributed Tokens

Drafting in progress…

Temporal Address Spaces

Drafting in progress…

Deterministic Synchronization

Drafting in progress…

Edge Computing

Drafting in progress…

Robotics

Drafting in progress…

Digital Twins

Drafting in progress…

Planetary Scale Simulation

Drafting in progress…

Part XI: Complete Projects

Introduction

The best way to learn MatterScript is to build increasingly ambitious systems.

Each project integrates every concept introduced earlier.

Drafting in progress…

Computational Biology

Simulating biological interactions using discrete digital rules rather than continuous differential equations (like Navier-Stokes or force fields) shifts the paradigm toward cellular automata, discrete agent-based modeling, and rule-based graph rewriting. By running this on WebGPU, you gain massive parallelism across millions or billions of discrete state transitions per second.

Here are several compelling problems in molecular biology that are notoriously hard for traditional physics-based models, but uniquely suited to a discrete, rule-based WebGPU runtime:

  1. Spatial Phase Separation & Biomolecular Condensates

The Problem: Membrane-less organelles (like stress granules or the nucleolus) form through Liquid-Liquid Phase Separation (LLPS). Traditional molecular dynamics (MD) cannot scale to the multi-micrometer, multi-second scales needed to see these form, while continuum mechanics fails to capture discrete binding stoichiometry.

Why Digital Rules Fit: You can model proteins as multi-valent discrete agents with local sticky/repulsive interaction rules. WebGPU’s grid/texture architecture lets you simulate millions of multivalency interactions simultaneously to study how phase boundaries emerge, how mutations alter saturation concentration, or how condensates partition specific RNA molecules.

  1. Chromatin Folding & Epigenetic Domain Formation

The Problem: DNA doesn’t just sit in the nucleus; it forms complex 3D loops and topological domains (TADs) driven by loop-extruding complexes (cohesin/CTCF) and epigenetic mark readers (e.g., HP1 binding methylated histones).

Why Digital Rules Fit: Loop extrusion is fundamentally a discrete motor process: a motor steps along a lattice, stalls at specific markers, or drops off. A 3D lattice automaton running on WebGPU can simulate chromatin as a linked string with discrete step, bind, and unbind rules. This allows you to test how local histone modifications globally reconfigure genome architecture in real time.

  1. Ribosome Jamming & Co-Translational Protein Folding

The Problem: Translation is not uniform. Ribosomes pause at rare codons or specific mRNA secondary structures, causing upstream ribosomal “traffic jams” that trigger Quality Control (RQC) mechanisms or alter how the emerging protein folds.

Why Digital Rules Fit: This is an asymmetric simple exclusion process (ASEP)—a classic discrete stochastic rule system. On WebGPU, you can simulate thousands of mRNAs in parallel, each being translated by multiple ribosomes with explicit rules for step rate, tRNA availability, collision detection, and nascent chain folding states.

  1. Viral Capsid Self-Assembly and Genome Packaging

The Problem: Viral capsids assemble from hundreds of identical protein subunits into highly symmetric geometries (like icosahedrons), often while selectively packaging viral RNA over host RNA. Traditional physics struggles with the long time horizons required for nucleation and defect correction.

Why Digital Rules Fit: Self-assembly is driven by directional geometric binding rules and state changes upon binding (e.g., conformational switching). You can assign each capsid subunit a set of discrete interaction faces and state-transition rules. WebGPU allows you to run massive stochastic ensembles to discover what specific local binding rules prevent “assembly traps” (malformed capsids).

  1. Multi-Enzyme Metabolic Channeling (Metabolons)

The Problem: In many metabolic pathways, sequential enzymes cluster into “metabolons” to hand off intermediates directly without letting them diffuse away into the cytoplasm. Understanding the efficiency thresholds of spatial enzyme clustering vs. free diffusion is a major open question in synthetic biology.

Why Digital Rules Fit: Instead of solving reaction-diffusion PDEs, you can treat metabolic intermediates as discrete particles diffusing on a 3D grid with probabilistic binding/reaction rules when colliding with enzyme nodes. This lets you optimize enzyme spatial configurations for synthetic biology constructs.

Target ProblemPrimary Rule MechanicsWebGPU Parallelism AdvantageKey Insight Unlocked
Biomolecular CondensatesMultivalent valence matching & sticky collisions3D grid / Voxel neighbor lookupsEmergent phase boundaries & stoichiometry
Chromatin Loop ExtrusionDirected stepping, stalling, & link constraintsArray/Buffer-driven parallel string updatesGlobal 3D genome reconfiguration
Ribosome Traffic JamsAsymmetric Simple Exclusion Process (ASEP)Parallel 1D array pipelines across cell transcriptsTranslational kinetics & RQC trigger limits
Capsid Self-AssemblyState-machine shape matching & local bondingParticle-agent state transitionsNucleation rates & structural defect control
Enzyme ChannelingRandom walks + discrete enzymatic state transitionsVoxel-based particle movement & collision checksOptimal spatial layout for synthetic pathways

Recommended Starting Point

If you want the quickest proof-of-concept that yields striking visual and quantitative results on WebGPU, start with Biomolecular Phase Separation (LLPS).

Because WebGPU natively maps 3D compute shaders to 3D textures, a 3D cellular automaton with multivalent binding rules will immediately produce visual condensates while letting you measure thermodynamic properties (like critical concentration and coarsening exponents) directly from compute buffers.

Drafting in progress…

Electrical Evolution

From Local Rules to Real-World Circuits

Electricity is one of the places where conventional computation and physical reality have become deeply entangled.

We normally describe electrical systems through equations: Ohm’s law, Kirchhoff’s laws, Maxwell’s equations, circuit models, differential equations, and numerical solvers. These descriptions are extraordinarily useful, but they begin with the behavior we want to describe and construct an abstract mathematical model of it.

MatterScript suggests another approach:

Don’t begin with the equations. Begin with the local transformations.

Instead of explicitly programming voltage, current, resistance, or electromagnetic fields, construct a computational universe in which small discrete elements interact according to simple neighborhood rules. Then observe what larger-scale electrical behavior emerges.

This makes electricity an ideal experimental domain for combining New Kind of Science, MatterScript, WebGPU, and the Invocation Language.


The Electrical Universe

Imagine an electrical substrate composed of discrete places. Each place has a small state and interacts only with its local neighborhood.

A simplified universe might contain states such as:

EMPTY
CHARGE
CONDUCTOR
INSULATOR
FIELD
DISCHARGING

The behavior of the system is determined not by a global equation, but by local transformations.

Conceptually:

             neighborhood
                  │
                  ▼
        ┌──────────────────┐
        │  local rule      │
        │                  │
        │ state + neighbors│
        └────────┬─────────┘
                 │
                 ▼
             next state

The important property is locality.

A place does not need to know the state of the entire electrical system. It only needs to respond to the nearby places with which it can interact.

This is the same fundamental idea used throughout MatterScript: global behavior is allowed to emerge from local causality.


1. Local Flow

The first experiment is deliberately simple.

Place a source at one boundary and a sink at another. Populate the space with conductive and insulating regions and establish local rules governing propagation.

SOURCE
  │
  ▼
┌───┬───┬───┬───┬───┐
│   │ → │ → │   │   │
├───┼───┼───┼───┼───┤
│   │ → │ → │ → │   │
├───┼───┼───┼───┼───┤
│   │   │ → │ → │   │
└───┴───┴───┴───┴───┘
                  │
                  ▼
                LOAD

No instruction says:

current = voltage / resistance

Instead, the local rules determine how an electrical state propagates from place to place.

The first question is simply:

Can stable electrical flow emerge from local transformations?

If it can, we have created the beginnings of an electrical computational universe without directly encoding the conventional abstraction of a circuit.


2. Emergent Resistance

The next experiment changes the geometry.

Introduce different arrangements of conductive and insulating places. Measure the resulting flow between the same source and sink.

For example:

Low resistance

SOURCE ───────────────── LOAD


Medium resistance

SOURCE ────────┐
               └───────── LOAD


High resistance

SOURCE ────┐
           │
           └───┐
               └──────── LOAD

The local rules have not necessarily changed.

The geometry has.

If the resulting macroscopic behavior exhibits a stable relationship between the imposed conditions and the observed flow, we have something very interesting:

Resistance has emerged from local interactions and spatial structure.

We did not need to encode Ohm’s law as the governing rule.

Ohm-like behavior becomes something we observe in the larger-scale system.

This is a fundamental distinction.

Traditional modeling often starts with:

equation → simulation → behavior

The MatterScript experiment starts with:

local rules → spatial evolution → behavior → measurement

The equation, if one appears, becomes a description of the observed behavior rather than the thing that generated it.


3. Branching Discharge

The third experiment moves from ordinary circuit behavior into phenomena that are naturally spatial.

Consider a highly charged region separated from ground:

             HIGH POTENTIAL
                   │
                   │
                   ?
                   │
                   │
                 GROUND

Give each place simple rules describing charging, ionization, conduction, and discharge.

Now let the system evolve.

We do not specify the shape of the discharge.

We do not tell the system to draw a lightning bolt.

We provide the local transformations and the boundary conditions.

If branching occurs, the familiar structure of lightning becomes an emergent spatial pattern.

                 /
                /
        ───────●
              / \
             /   \
            /     ───
           /
      ────●

Change the local rule and the branching morphology may change.

Change the geometry and the preferred paths may change.

Change the boundary conditions and the entire pattern may reorganize.

This is precisely the kind of experiment associated with a New Kind of Science approach:

A complex natural phenomenon may be the large-scale consequence of remarkably simple local rules.

The objective is not necessarily to reproduce every detail of physical lightning immediately. The objective is to discover what kinds of electrical structures can emerge from small computational rules.


4. Self-Organizing Circuits

The fourth experiment reverses the normal circuit-design process.

Instead of designing a circuit and then simulating it, give the system a desired electrical behavior and allow the spatial system to search for configurations that produce it.

For example:

SOURCE ───────────────── LOAD

is the requirement.

The internal geometry is unknown.

The system explores possible arrangements of conductive, insulating, and stateful regions:

┌───────────────────────────────┐
│ ███   ··· ████   ···         │
│   ████      ███   ███        │
│ ···   ███      █████          │
│      ███████        ███       │
│ ███       ███████████         │
└───────────────────────────────┘

The resulting topology might look nothing like a conventional circuit diagram.

That is not a failure.

It is the point.

The system is not being asked to reproduce a human-designed circuit. It is being asked to discover a spatial organization capable of producing the desired behavior.

This raises a deeper question:

Can computation discover electrical machinery rather than merely calculate its behavior?


WebGPU: Scaling the Electrical Universe

A major advantage of this approach is that local rules are naturally parallel.

A conventional numerical simulation may ask a processor to repeatedly solve large systems of equations.

A MatterScript electrical universe can instead be represented as a large collection of places:

┌───┬───┬───┬───┬───┬───┐
│   │   │   │   │   │   │
├───┼───┼───┼───┼───┼───┤
│   │   │   │   │   │   │
├───┼───┼───┼───┼───┼───┤
│   │   │   │   │   │   │
├───┼───┼───┼───┼───┼───┤
│   │   │   │   │   │   │
└───┴───┴───┴───┴───┴───┘

Each place can apply the same neighborhood transformation independently.

This maps naturally onto the grid and texture architecture of a GPU.

Instead of thinking:

Solve one enormous equation.

we can think:

Apply one local transformation to millions of places simultaneously.

WebGPU therefore provides a practical computational substrate for exploring large MatterScript universes.

The scale is important because many interesting physical phenomena are not visible in a tiny system. Large-scale structures emerge only after many local interactions have had an opportunity to interact.


From Model to Machine

This is where the experiment becomes specifically MatterScript, rather than merely another cellular-automata simulation.

The computational model can progress through several representations:

       Local rules
            │
            ▼
       MatterScript
            │
            ▼
     WebGPU simulation
            │
            ▼
   Emergent spatial structure
            │
            ▼
   Compiled spatial machinery
            │
            ▼
     Invocation Language
            │
            ▼
       Physical target

The crucial idea is that the simulation and the deployed machinery do not have to be unrelated programs.

The same description can have different targets.

A simulation target can be used to explore the behavior.

A computational target can be used to execute the resulting machinery.

A hardware target can potentially turn the spatial organization into a physical implementation.

The model therefore does not merely describe a circuit.

It can become the machinery from which the circuit is constructed.


Invocation: From Simulation to Silicon

The Invocation Language provides the boundary between the MatterScript computational universe and an external target.

Conceptually:

@target(...)

does not simply mean:

“Run this program somewhere else.”

It establishes a relationship between the spatial machinery described by MatterScript and another computational environment.

This opens the possibility of a progression such as:

MatterScript model
       ↓
simulation
       ↓
discover behavior
       ↓
freeze spatial organization
       ↓
compile machinery
       ↓
invoke target
       ↓
physical electrical system

The important concept here is placement.

When a spatial arrangement has been discovered and compiled, its geometry can become a form of frozen knowledge.

Instead of repeatedly calculating the same relationships at runtime, the relationships themselves become part of the machinery.

This is one reason electrical systems are such an interesting target for MatterScript: electrical behavior is inherently spatial.


Simulation and Reality as Different Targets

The same computational description can therefore be viewed through different targets:

                 MATTERSCRIPT
                      │
             local transformations
                      │
          ┌───────────┴───────────┐
          │                       │
      SIMULATION               HARDWARE
          │                       │
      observe                  operate
      measure                  physically
          │                       │
          └───────────┬───────────┘
                      │
                 same machinery

The simulation is not necessarily the final product.

It is one environment in which the machinery can be explored.

The hardware is another environment in which that machinery can exist.

This suggests a different relationship between simulation and engineering:

Simulation can become a stage in the compilation of physical machinery.


What This Demonstrates

The Electrical Evolution project is valuable not because it replaces electrical engineering overnight, but because it demonstrates a different way of asking electrical questions.

A single local rule set may produce many macroscopic phenomena.

Rather than creating a separate mathematical abstraction for every phenomenon, we can search for simple transformations capable of producing families of behaviors.

Complex electrical behavior can emerge from simple interactions.

Flow, resistance, breakdown, oscillation, branching, and other structures can become things we observe rather than things we explicitly prescribe.

Geometry becomes part of computation.

A circuit is not merely a collection of equations. Its physical arrangement affects what it can do.

MatterScript makes that arrangement computationally explicit.

Massive parallelism becomes natural.

A neighborhood rule can be applied simultaneously across millions of places, making GPU architectures particularly appropriate for these experiments.

Discovery and deployment can become connected.

The spatial organization discovered during computation can potentially become compiled machinery rather than merely a visualization of a solution.

Invocation provides the bridge.

The computational universe does not have to end at the simulation boundary.

The same machinery can be directed toward different computational and physical targets.


Beyond Circuits

Once this framework exists, the electrical experiments can expand considerably.

The same approach can investigate:

Ohm-like behavior
       ↓
Kirchhoff-like conservation
       ↓
RC dynamics
       ↓
Inductive behavior
       ↓
Directional conduction
       ↓
Transistor-like interaction
       ↓
Oscillation
       ↓
Transmission phenomena
       ↓
Electrical breakdown
       ↓
Branching discharge
       ↓
Network dynamics
       ↓
Self-organizing circuits

The objective is not to encode each phenomenon independently.

The more interesting objective is to discover whether families of electrical behavior can emerge from a comparatively small computational universe.

That is the New Kind of Science question applied to electricity.


The Larger Experiment

Electrical Evolution ultimately asks a question that extends beyond electrical engineering:

Can a physical system be understood as a computational universe of local transformations, and can the resulting computational machinery be invoked back into the physical world?

If the answer is yes, then the boundary between simulation, programming, and physical construction begins to change.

Traditional engineering often follows:

physical phenomenon
        ↓
mathematical model
        ↓
numerical simulation
        ↓
engineering design
        ↓
physical implementation

MatterScript proposes an alternative path:

physical phenomenon
        ↓
local transformations
        ↓
spatial computational universe
        ↓
emergent behavior
        ↓
compiled spatial machinery
        ↓
invocation
        ↓
physical implementation

The difference is subtle but fundamental.

The equations describe the behavior.

The rules generate the behavior.

And in MatterScript, the spatial organization generated by those rules can itself become a form of computation.

Local rules. Global behavior. Physical machinery.

Drafting in progress…

Model-Free Inference

Theory-free (or model-free) inference in Cellular Automata (CA) involves discovering local update rules directly from observed spatial transitions, without assuming pre-existing equations, continuous physics, or domain models. Integrating this directly into MatterScript—where asynchronous computation, process expressions, and physical geometry merge—opens up powerful paradigms for self-adjusting hardware, autonomous spatial runtime, and generative architecture.

1. Dynamic Process Reconstruction from Spatial Observations

  • Observed State to Invocation Syntax: Instead of hand-authoring process expressions or 2NCL (Null Convention Logic) transition tables, MatterScript can use spatial snapshots of physical/digital state updates (Ct → Ct+1) to synthesize its local transition logic on the fly.
  • Agnostic Logic Synthesis: The compiler or runtime can treat unknown physical cellular environments as a black box, extracting the minimal spatial lookup table or asynchronous firing rules required to replicate observed state propagation.

2. Runtime Adaptive Hardware & Self-Healing Logic

  • In-Situ Reconfiguration: When deployed onto FPGAs or asynchronous hardware substrates, an integrated theory-free engine allows MatterScript modules to adapt to unmodeled physical variations, signal delays, or damaged routing channels.
  • Observation-Driven Calibration: By continuously sampling local neighborhood transitions, the runtime can infer corrected logic tables or phase-handshake rules dynamically, maintaining functional equivalence without needing an accurate physical model of the underlying hardware defect.

3. Inverse Geometry & Material Synthesis

  • Generative Spatial Rules: In MatterScript, where spatial geometry directly drives computational processes, theory-free inference enables “target-driven” design. You provide sequences of desired physical or topological evolutions (e.g., self-assembling structures or wave-front distributions), and the inference engine reverse-engineers the precise local CA rules needed to produce that macro-level geometry.
  • Emergent Behavior Extraction: Complex global behavior can be distilled down to simple, executable MatterScript process expressions, bypassing the need to solve complex systems of partial differential equations (PDEs) or continuous fluid dynamics.

4. Dual-Rail & Asynchronous Constraint Inference

  • Probabilistic to Deterministic Mapping: Since asynchronous logic relies on completion signals and valid/null state transitions, theory-free inference can observe raw spatial state sequences to identify valid neighborhood transitions and generate noise-tolerant, hazard-free asynchronous state machines automatically.
  • Neighborhood Scale Reduction: Inference algorithms can iteratively evaluate the minimal necessary spatial radius (r) and state depth (k) required for a local process expression, minimizing the fan-in and lookup table size on hardware target blocks.

Drafting in progress…

MatterScript and the Hacker Fab: Spatial Hardware Synthesis and Fabrication Integration

MatterScript is designed around a premise that becomes especially powerful when applied to the Hacker Fab:

Physical structure is part of computation.

The Hacker Fab is an open-source semiconductor fabrication ecosystem whose goal is to make semiconductor fabrication accessible, reproducible, inexpensive, and understandable from first principles. Its workflow already spans electronic design automation, mask generation, fabrication tools, process development, device modeling, packaging, and electrical characterization. The project explicitly emphasizes rapid iteration, documentation, simple designs, in-situ sensing, and eventually a closed-loop process from wafer fabrication through measurement.

MatterScript provides a natural computational language for this environment because its fundamental abstraction is neither the conventional software function nor the conventional circuit schematic.

It is the space-time process.

A MatterScript description can represent:

  • where computational elements exist,
  • how they are connected,
  • how events propagate,
  • how physical geometry constrains behavior,
  • how local state changes,
  • how measurements become inputs,
  • and how a physical process can be represented as a computation.

The Hacker Fab integration therefore does not treat MatterScript as another FPGA programming language.

Instead, MatterScript becomes a spatial intermediate language connecting semiconductor design, fabrication, machine automation, and measurement.

                         MatterScript
                              │
                    Space-Time Model
                              │
          ┌───────────────────┼───────────────────┐
          │                   │                   │
          ▼                   ▼                   ▼
      Circuit IR          Geometry IR        Process IR
          │                   │                   │
          ▼                   ▼                   ▼
      SPICE/KiCad       PHIDL / GDSII       Fab Jobs
          │                   │                   │
          └──────────────┬────┴───────────────────┘
                         ▼
                  Hacker Fab Process
                         │
              ┌──────────┴──────────┐
              ▼                     ▼
        Fabricated Device       Metrology
              │                     │
              └──────────┬──────────┘
                         ▼
                  Measured Data
                         │
                         ▼
                  Model / Inference
                         │
                         └──────────────↺

This creates the possibility of treating the entire fab as a programmable computational system.


1. The Hacker Fab as a Computational Substrate

The Hacker Fab’s stated objective is not simply to reproduce conventional semiconductor manufacturing.

Its design philosophy emphasizes inexpensive tools, simple designs, reproducibility, first-principles understanding, and rapid iteration. The documentation explicitly identifies fast hardware development and closed-loop operation as important design goals.

That philosophy closely matches MatterScript.

Traditional semiconductor design separates the workflow into specialized tools:

Schematic
   ↓
Simulation
   ↓
Layout
   ↓
DRC
   ↓
Mask
   ↓
Fabrication
   ↓
Measurement

Each stage has its own representation.

MatterScript proposes a different architecture:

                 MatterScript
                      │
              spatial process model
                      │
        ┌─────────────┼─────────────┐
        ▼             ▼             ▼
     circuit       geometry       process
        │             │             │
        ▼             ▼             ▼
      SPICE         GDSII        machine job
        │             │             │
        └─────────────┼─────────────┘
                      ▼
                  fabricated
                    device
                      │
                      ▼
                   measured
                      │
                      ▼
                  new state

The representations still exist.

MatterScript simply establishes a common semantic layer between them.


2. The Correct Integration Boundary

MatterScript should not replace PHIDL, KiCad, ngspice, the Hacker Fab mask-generation software, or the machine-control infrastructure.

Instead, each existing tool remains responsible for the domain it already handles.

The integration boundary should look like this:

MatterScript
     │
     ▼
MatterScript Intermediate Representation
     │
     ├──────────────► Circuit Backend
     │                    │
     │                    ▼
     │                 SPICE
     │
     ├──────────────► Layout Backend
     │                    │
     │                    ▼
     │              PHIDL / GDSII
     │
     ├──────────────► Fabrication Backend
     │                    │
     │                    ▼
     │             Hacker Fab Jobs API
     │
     └──────────────► Analysis Backend
                          │
                          ▼
                    Measurement Data

This follows the Hacker Fab’s existing philosophy of using simple, composable tools rather than introducing unnecessary infrastructure.


3. MatterScript as a Spatial Intermediate Representation

The most important component of the integration is the MatterScript intermediate representation, or Matter IR.

A MatterScript source program describes processes:

<>source
    |
    ▼
<>transform
    |
    ▼
$output

The compiler converts these into explicit objects:

Place
Process
Token
Event
Connection
Geometry
Material
Device
Measurement

A simplified internal representation might look like:

Cell {
    name: "inverter"
    position: (100um, 200um)
    dimensions: (40um, 20um)

    inputs:
        A

    outputs:
        Y

    devices:
        NMOS
        PMOS

    processes:
        evaluate
        discharge
        charge
}

The important property is that the geometry is not added later.

It exists in the intermediate representation from the beginning.

That allows the same object to be interpreted as:

  • a computational process,
  • a circuit,
  • a physical layout,
  • a fabrication structure,
  • and a measurable device.

4. Mapping MatterScript to Hacker Fab EDA

The Hacker Fab already has an EDA effort divided into areas including device modeling and DRC/mask design. The mask automation work uses PHIDL and produces GDSII layouts, while the device-modeling work has explored KiCad and its embedded ngspice simulation environment.

These become the first two major MatterScript compiler targets.

MatterScript
      │
      ▼
   Matter IR
      │
      ├──────────────┐
      │              │
      ▼              ▼
 Circuit IR       Layout IR
      │              │
      ▼              ▼
 KiCad/SPICE      PHIDL
      │              │
      ▼              ▼
  simulation       GDSII

This means that a MatterScript program could eventually describe a physical circuit once and derive both:

  1. the electrical model used for simulation, and
  2. the physical geometry used to fabricate it.

5. The matter-spice Backend

The first circuit-oriented backend should target SPICE.

The purpose of matter-spice is to lower MatterScript processes into a conventional electrical simulation representation.

For example:

  input
    |
    ▼
 inverter
    |
    ▼
  output

could produce a SPICE-level representation containing:

VIN
 |
 ├── PMOS
 │
 └── NMOS
 |
GND

The backend should generate a standard netlist rather than inventing a new simulator.

This allows Hacker Fab developers to continue using existing SPICE infrastructure.

The resulting pipeline becomes:

main.ms
   │
   ▼
matter-spice
   │
   ▼
circuit.spice
   │
   ▼
ngspice
   │
   ▼
waveforms

The Hacker Fab device-modeling work already uses KiCad and ngspice for circuit simulation, making this a particularly natural integration point.


6. Device Models as MatterScript Components

A physical MOSFET is more than a symbol.

It has:

  • geometry,
  • material properties,
  • process-dependent parameters,
  • electrical behavior,
  • fabrication constraints,
  • and measured characteristics.

MatterScript can represent this as a component whose behavioral and physical descriptions are connected.

For example:

device NMOS {
    geometry {
        channel_length = 10um
        channel_width  = 10um
    }

    terminals {
        gate
        source
        drain
        body
    }

    behavior {
        model = mosfet
    }
}

The SPICE backend can interpret behavior.

The layout backend can interpret geometry.

The fabrication backend can interpret the process requirements.

The measurement system can associate measured device data back with the same device identifier.

This produces a much stronger connection than the traditional separation between schematic symbols and physical layout cells.


7. The matter-phidl Backend

The second major backend should target PHIDL.

This is particularly appropriate because the Hacker Fab’s mask-generation work already uses PHIDL for programmable mask design. The project has explicitly explored automated component instances, parameterized dimensions, DRC rules, and GDSII export.

MatterScript geometry can therefore map directly into PHIDL objects.

For example:

device inverter {
    position = (100um, 200um)

    nmos {
        width = 10um
        length = 2um
    }

    pmos {
        width = 20um
        length = 2um
    }
}

could produce a PHIDL cell containing the required polygons.

Conceptually:

MatterScript
      │
      ▼
   Layout IR
      │
      ▼
 matter-phidl
      │
      ▼
 PHIDL geometry
      │
      ▼
    GDSII

The resulting GDSII file becomes the artifact handed to the existing Hacker Fab mask workflow.


8. Geometry as a First-Class Compiler Output

This is where MatterScript differs most strongly from conventional programming languages.

Suppose the source contains:

A @ (0,0)
B @ (20um,0)
C @ (40um,0)

A conventional compiler may regard these positions as irrelevant.

MatterScript cannot.

The positions can determine:

  • transistor placement,
  • routing,
  • device spacing,
  • proximity,
  • mask geometry,
  • fabrication constraints,
  • process timing,
  • and visualization.

The compiler should therefore preserve them:

Matter IR
    │
    ├── logical graph
    │
    ├── physical coordinates
    │
    ├── device dimensions
    │
    └── layer assignments

The PHIDL backend can then construct the corresponding geometry.


9. DRC as a Compiler Phase

The Hacker Fab’s mask automation work specifically identifies DRC as a future component of the automated layout workflow.

MatterScript should treat DRC not merely as an external post-processing step, but as a compilation phase.

The pipeline becomes:

MatterScript
      │
      ▼
Layout IR
      │
      ▼
Geometry generation
      │
      ▼
DRC
      │
 ┌────┴────┐
 │         │
pass      fail
 │         │
 ▼         ▼
GDSII     compiler error

For example:

error: NMOS source/drain spacing violation

device: M14
layer: diffusion
required: >= 4.0um
actual:    2.7um

source:
    src/amplifier.ms:184

This is much more useful than reporting an anonymous polygon violation.

The compiler knows why the geometry exists.


10. From DRC to Process Rules

The longer-term goal should be to make fabrication rules part of the MatterScript target description.

For example:

@process(hackerfab-v1)

rule diffusion_spacing >= 4um
rule metal_spacing     >= 3um
rule contact_size      = 5um

These constraints can affect code generation before GDSII exists.

That makes fabrication constraints analogous to type constraints in a programming language.

A MatterScript program is not simply:

syntactically valid

It must eventually be:

physically realizable under the selected fabrication process.


11. Process-Aware Compilation

A MatterScript design should therefore identify its fabrication process.

For example:

project amplifier {
    process = "hackerfab"
}

The compiler loads the corresponding process description:

process/
├── layers
├── dimensions
├── materials
├── spacing
├── device_templates
├── fabrication_steps
└── measurement_requirements

The exact process schema can evolve as Hacker Fab’s process documentation evolves.

This creates a clean separation:

MatterScript design
        │
        ▼
process-independent model
        │
        ▼
Hacker Fab process definition
        │
        ▼
fabricatable layout

The same design could eventually be retargeted to different process variants.


12. Fabrication as a Build

A particularly powerful consequence is that fabrication itself can become part of the build graph.

Today, a software build might look like:

source
  ↓
compile
  ↓
link
  ↓
binary

A Hacker Fab MatterScript build could look like:

MatterScript
      ↓
simulate
      ↓
generate layout
      ↓
     DRC
      ↓
    GDSII
      ↓
mask artifact
      ↓
fabrication job
      ↓
fabricated wafer
      ↓
measurement

This does not mean that matter build should automatically turn on a machine.

Instead, fabrication should become an explicit artifact and job boundary:

matter build --target gds
matter verify
matter package
matter submit

The final submit operation can create a Hacker Fab machine job.


13. Integration With Hacker Fab Machine Integration

This is where the Hacker Fab’s existing infrastructure becomes especially valuable.

The Hacker Fab machine-integration project already defines a generalized communication system between the web application and individual fabrication tools. The architecture uses a centralized job queue, APIs, file storage, and local Raspberry Pi controllers.

The existing model is essentially:

Web Application
       │
       ▼
    Job API
       │
       ▼
   Jobs Queue
       │
       ▼
 Raspberry Pi
       │
       ▼
    Machine

MatterScript can become a producer of those jobs.

MatterScript
       │
       ▼
Fabrication Artifact
       │
       ▼
MatterScript Job Adapter
       │
       ▼
Hacker Fab Jobs API
       │
       ▼
Machine Queue
       │
       ▼
Tool Controller
       │
       ▼
Fabrication Tool

This is much more aligned with the actual Hacker Fab ecosystem than attempting to introduce a completely separate hardware-runtime architecture.


14. Job Artifacts

A MatterScript fabrication job should contain both parameters and files.

For example:

{
    "machine": "stepper",
    "input_parameters": {
        "design": "inverter_v3",
        "process": "hackerfab-v1",
        "wafer_id": "wafer-042",
        "orientation": "default"
    },
    "artifacts": {
        "gds": "uploads/abc123",
        "manifest": "uploads/def456"
    }
}

This matches the Hacker Fab machine-integration architecture, where jobs contain machine-specific input parameters and larger files can be stored externally and referenced through an object-storage key. The documented stepper workflow already uses this general pattern for transferring image files to the machine.

MatterScript should therefore reuse the same mechanism.

It should not invent another file-transfer system.


15. The matter-fab Adapter

A matter-fab backend can translate a MatterScript build into a Hacker Fab job.

For example:

matter fab submit \
    --machine stepper \
    --design inverter_v3

The adapter would:

  1. validate the MatterScript project,
  2. generate the required layout,
  3. run DRC,
  4. generate GDSII,
  5. generate a fabrication manifest,
  6. upload large artifacts,
  7. create a machine job,
  8. receive the job ID,
  9. track completion.

Conceptually:

matter fab submit
       │
       ├── compile
       ├── DRC
       ├── generate GDSII
       ├── upload
       └── POST /jobs
                    │
                    ▼
                 job_id

The Hacker Fab job system already defines concepts such as machine, status, input parameters, output parameters, timestamp, and priority.

MatterScript can preserve those concepts rather than creating parallel infrastructure.


16. Human-in-the-Loop Is a Feature

The Hacker Fab machine-integration documentation explicitly describes a deliberate human-in-the-loop design, including local GUI approval, physical controls, and offline operation.

MatterScript should preserve this property.

A submitted fabrication job should therefore have states such as:

GENERATED
    ↓
VERIFIED
    ↓
QUEUED
    ↓
AWAITING_APPROVAL
    ↓
RUNNING
    ↓
COMPLETED

The compiler should never assume that:

GDSII exists

means:

chip is being fabricated

There is an important physical boundary between software intent and physical action.

MatterScript can automate the former while leaving the latter explicitly controlled.


17. The Stepper as the First Demonstration

The Hacker Fab lithography stepper is an especially useful first integration target because the existing machine-integration architecture already demonstrates file-based job submission to a patterning machine.

The first MatterScript/Hacker Fab demonstration could therefore be extremely concrete:

MatterScript
     │
     ▼
simple transistor layout
     │
     ▼
   PHIDL
     │
     ▼
   GDSII
     │
     ▼
    DRC
     │
     ▼
stepper job
     │
     ▼
physical pattern

This establishes the complete chain without requiring MatterScript to control the fabrication machine directly.


18. MatterScript and the Fab Toolchain

The larger architecture becomes:

                     MatterScript
                          │
                     Matter IR
                          │
        ┌─────────────────┼──────────────────┐
        │                 │                  │
        ▼                 ▼                  ▼
    matter-spice     matter-phidl       matter-fab
        │                 │                  │
        ▼                 ▼                  ▼
    KiCad/SPICE        GDSII             Job API
        │                 │                  │
        ▼                 ▼                  ▼
   Device model        Mask data       Fab machine
        │                 │                  │
        └─────────────────┼──────────────────┘
                          ▼
                    Physical device
                          │
                          ▼
                      Metrology
                          │
                          ▼
                     Measurement

This is the central Hacker Fab integration architecture.


19. Measurement as an Input Language

The final part of the loop is measurement.

A fabricated transistor might produce:

VGS
VDS
IDS

A conventional workflow exports those measurements as CSV files and analyzes them separately.

MatterScript can instead treat them as a spatial state transition.

For example:

measurement {
    device = M14

    sweep {
        VGS = 0..5V
        VDS = 0..5V
    }

    outputs {
        IDS
    }
}

The result can be represented as a MatterScript data structure:

M14:
    state_0 → state_1
    state_1 → state_2
    ...

This creates a direct connection between physical measurement and model inference.


20. Closing the Loop With Device Modeling

The Hacker Fab device-modeling work already investigates extracting transistor parameters from electrical data and feeding those parameters back into SPICE models.

MatterScript can formalize that loop.

Fabricated transistor
        │
        ▼
Electrical measurement
        │
        ▼
Measurement dataset
        │
        ▼
Parameter extraction
        │
        ▼
MatterScript device model
        │
        ▼
SPICE simulation
        │
        ▼
New circuit design
        │
        ▼
New layout
        │
        ▼
New fabrication

This is precisely the sort of iterative, closed-loop hardware development that the Hacker Fab’s design philosophy encourages.


21. Model-Free Inference

MatterScript adds another possibility beyond conventional parameter fitting.

Instead of assuming that the device behavior must conform to a predetermined mathematical model, measured transitions can be treated as observations of a physical state machine.

For example:

Input State
     │
     ▼
Physical Device
     │
     ▼
Measured State

Repeated observations produce:

A → B
A → B
A → C
A → B

The system can determine:

A → B

as the dominant observed transition while retaining uncertainty around the exceptional transition.

This provides a bridge between MatterScript’s cellular-automata/model-free inference work and Hacker Fab’s empirical device characterization.

The physical chip becomes a source of computational rules.


22. Fabrication Tools as Spatial Computers

This perspective can be extended beyond the chip itself.

The Hacker Fab consists of physical machines:

Stepper
Spin Coater
Evaporator
Sputtering System
Furnace
Etcher
Probe Station

Each machine has:

  • state,
  • geometry,
  • timing,
  • inputs,
  • outputs,
  • sensors,
  • and processes.

The Hacker Fab’s machine-integration system already treats these tools as programmable devices with jobs and state transitions.

MatterScript can provide a common representation for those transitions.

For example:

  wafer_loaded
       ↓
      spin
       ↓
  coat_complete
       ↓
      bake
       ↓
    pattern
       ↓
   wafer_ready

The same language that describes a computational circuit can therefore describe the workflow that creates that circuit.


23. From Machine Jobs to Process Expressions

A machine job can be viewed as a MatterScript process.

For example:

process spin_coat {
    input:
        wafer

    parameters:
        rpm
        duration

    output:
        coated_wafer
}

The Hacker Fab adapter can lower this to:

POST /jobs

with:

{
    "machine": "spincoater",
    "input_parameters": {
        "rpm": 3000,
        "duration": 30
    }
}

The machine controller performs the physical operation and reports:

COMPLETED

The MatterScript runtime then converts that completion into an event.

machine completion
        │
        ▼
  coat_complete
        │
        ▼
  next process

This creates a unified language for both computation and fabrication workflow.


24. The Fabrication Graph

At this point, the entire semiconductor-development process can be represented as a graph.

              Device Design
                   │
                   ▼
              Simulation
                   │
                   ▼
                Layout
                   │
                   ▼
                  DRC
                   │
                   ▼
                 Mask
                   │
                   ▼
             Lithography
                   │
                   ▼
               Deposition
                   │
                   ▼
                 Etch
                   │
                   ▼
                Doping
                   │
                   ▼
               Packaging
                   │
                   ▼
              Measurement
                   │
                   ▼
             Device Model
                   │
                   └──────────↺

MatterScript’s process model is naturally suited to expressing this graph.

The difference is that the graph is not merely documentation.

It becomes executable infrastructure.


25. Repository Structure

A MatterScript project integrated into Hacker Fab should remain compatible with the organization’s existing open-source development practices.

A proposed project structure is:

project/
│
├── src/
│   ├── devices.ms
│   ├── circuits.ms
│   ├── geometry.ms
│   └── process.ms
│
├── process/
│   ├── hackerfab-v1.toml
│   ├── layers.toml
│   └── rules.toml
│
├── layout/
│   ├── generated/
│   └── gds/
│
├── simulation/
│   ├── spice/
│   └── data/
│
├── fabrication/
│   ├── manifests/
│   └── jobs/
│
├── measurements/
│   ├── raw/
│   └── processed/
│
├── build.zig
└── matter.toml

The generated artifacts should be distinguishable from source artifacts.

For example:

src/       ← human-authored MatterScript
process/   ← fabrication knowledge
layout/    ← generated physical design
simulation/← generated/experimental electrical models
fabrication/← machine-job artifacts
measurements/← empirical results

26. Reproducibility

Reproducibility is especially important in semiconductor fabrication.

Every MatterScript build should record:

source revision
compiler version
process version
DRC rules
device models
layout generator version
GDSII checksum
simulation parameters
fabrication job ID
wafer/device identifiers
measurement dataset

A build manifest could therefore connect software and physical artifacts:

{
    "design": "inverter-v3",
    "source_revision": "abc123",
    "process": "hackerfab-v1",
    "layout": "sha256:...",
    "simulation": "sha256:...",
    "fabrication_job": "HF-004281",
    "wafer": "W12",
    "devices": ["M14", "M15"],
    "measurement_dataset": "measurements/W12.csv"
}

This is important because the physical artifact cannot be reproduced merely from the source code if the process version and layout rules have changed.


27. Open-Source Compatibility

The Hacker Fab uses a deliberately open licensing structure: hardware contributions default to CERN-OHL-W, software to MPL-2.0, and documentation to CC BY-SA 4.0, subject to the project’s contribution and provenance rules.

MatterScript integration should respect that structure.

The compiler itself can remain a software project.

Generated hardware can carry appropriate attribution and licensing metadata.

Documentation can reference the corresponding MatterScript source and generated artifacts.

A generated design manifest could therefore contain:

source_license
hardware_license
documentation_license
upstream_components

This is especially important when MatterScript begins generating reusable physical components.


28. The First Practical Prototype

The complete architecture is deliberately larger than the first implementation.

The first Hacker Fab/MatterScript prototype should be much smaller.

The recommended milestone is:

MatterScript
      │
      ▼
simple NMOS/inverter model
      │
      ├──────────────► SPICE
      │                  │
      │                  ▼
      │              simulation
      │
      └──────────────► PHIDL
                         │
                         ▼
                       GDSII
                         │
                         ▼
                        DRC

Once this works, add:

Phase 1
    MatterScript → SPICE

Phase 2
    MatterScript → PHIDL → GDSII

Phase 3
    MatterScript → DRC-aware GDSII

Phase 4
    GDSII → Hacker Fab machine job

Phase 5
    Machine completion → MatterScript event

Phase 6
    Physical measurement → MatterScript dataset

Phase 7
    Measurement → model inference

Phase 8
    Model → next design iteration

The important thing is that every phase reuses the same semantic model.


29. The Complete Hacker Fab/MatterScript Loop

The finished architecture looks like this:

                         MatterScript
                              │
                              ▼
                    Space-Time Model
                              │
                              ▼
                    Matter Intermediate
                       Representation
                              │
          ┌───────────────────┼───────────────────┐
          │                   │                   │
          ▼                   ▼                   ▼
      Circuit IR          Layout IR          Process IR
          │                   │                   │
          ▼                   ▼                   ▼
       SPICE              PHIDL/GDSII        Fab Jobs
          │                   │                   │
          ▼                   ▼                   ▼
     Simulation             DRC             Hacker Fab
                                                  │
                                                  ▼
                                             Fabrication
                                                  │
                                                  ▼
                                              Metrology
                                                  │
                                                  ▼
                                            Measurement
                                                  │
                                                  ▼
                                         Model Inference
                                                  │
                                                  ▼
                                           Device Model
                                                  │
                                                  └──────↺

This is the deeper purpose of the integration.

MatterScript does not merely provide another way to draw a circuit.

It provides a computational representation of the relationship between the circuit, its geometry, its fabrication process, and its observed physical behavior.

The Hacker Fab already supplies the physical infrastructure necessary to close that loop: programmable mask generation, simulation and device modeling, fabrication tools, machine integration, measurement, and a documentation culture centered around reproducible open hardware.

MatterScript can provide the missing connective tissue.

The design is a process. The process has geometry. The geometry becomes a device. The device becomes a measurement. The measurement becomes knowledge. And the knowledge becomes the next design.

That is the Hacker Fab integration target for MatterScript.

Drafting in progress…

Autonomous Vehicle Sensor Model

Drafting in progress…

CPU Cache Simulation

Drafting in progress…

Smart Factory

Drafting in progress…

Drone Swarm

Drafting in progress…

Automotive Radiator

Drafting in progress…

Digital Earth

Drafting in progress…

Appendix A: Language Reference

1. Top-Level Structure & Definitions

Definition Declarations

Definitions are declared using a name followed immediately by square brackets [...]. The brackets enclose the definition’s body, including its sources, destinations, field of resolution, domain specifier, and transform rules.

  • Syntax: NAME[(sources)(destinations) domain_specifier : body]
  • Identifier Identifier: Any standard alphanumeric identifier preceding [ is classified as a definition name (entity.name.type.definition).

Domain Specifier

The resolution/domain specifier section defines the operational domain or spatial context of the definition.

  • Domain Keywords: Currently, the set includes:
  • spatial3d

2. Invocations & Place References

Invocations

Invocations execute/instantiate named relationships or functions.

  • Syntax: NAME($dest1 $dest2)(source1<> source2<>)
  • Identifier: Any identifier preceding ( is classified as an invocation (entity.name.function.invocation).

Destination Places

Destination places or dynamic name compositions are prefixed with a dollar sign $.

  • Syntax: $identifier
  • Token Classification: variable.parameter.destination
  • Pattern: \$[A-Za-z_][A-Za-z0-9_]*

Source Places

Source places represent incoming causal signals or inputs and are denoted by trailing angle brackets that enclose the content or reference to connect to that signal <>.

  • Syntax: name<> or name<content>
  • Token Classification: entity.name.function.place
  • Pattern: NAME(?=\s*<)

3. Control Keywords & Built-in Functions

Procedural / Code Generation Keywords

  • generate: Declares code-generation blocks (keyword.control).

Structure Field Labels

Key-value declarations preceding a colon ::

  • inputs:
  • output:
  • const:

Built-in Support Functions

Functions expecting argument lists in parentheses:

  • clamp(...)
  • avg(...)

4. Lexical Tokens & Punctuation

Comments

  • Line Comment: Double slashes // ignore all remaining text on the line.

Literals & Arithmetic

  • Numeric Constants: Unsigned integer literals (\b[0-9]+\b).
  • Arithmetic Operators: Standard mathematical operations: +, -, *, /.

Structural Delimiters & Delimiting Groups

DelimiterToken NameStructural Purpose
{ }punctuation.definition.group.mutexEncloses mutex or conditional completeness groups.
[ ]punctuation.definition.bracketEncloses bundle brackets or definition outer bodies.
( )punctuation.definition.parenEncloses parameter lists and source/destination bindings.
< >punctuation.definition.placeEnclose source place boundaries.
:punctuation.separator.colonSeparates domain specifiers from bodies, or key-value entries.
,punctuation.separator.commaSeparates itemized arguments or entries.

Drafting in progress…

Appendix B: Grammar Specification

Drafting in progress…

Appendix C: Compiler Internals

Drafting in progress…

Appendix D: Standard Library Reference

Drafting in progress…

Appendix E: Invocation Language Reference

Drafting in progress…

Appendix F: Mathematics of Geometry-Aware Placement

Drafting in progress…

Appendix G: Building the Compiler

Drafting in progress…

Appendix H: Future Directions

Drafting in progress…