Launched Logik v1
Successfully launched the first major release of Logik, an educational computational logic framework built in Java. It allows users to build and simulate complex logic circuits.
A from-scratch Raft consensus library in Rust, paired with a fault-tolerant key–value store over gRPC.
A working implementation of the Raft consensus algorithm, built from scratch in Rust. The core Raft protocol — leader election, log replication, and safety — runs inside a single-threaded async event loop and sits underneath a replicated key–value store accessible over gRPC.
Raft is a protocol for getting a group of machines to agree on a sequence of commands, even when some of them crash or lose connectivity. All three subproblems are tackled separately:
On top of Raft sits a concurrent key–value store backed by a DashMap. Clients call Put, Get, and Delete via gRPC. Reads are served locally from whichever replica you hit; writes go through the leader and are replicated before being acknowledged.
Every log entry is written to a JSON-line file (the write-ahead log) before a client gets a response. On restart the log is replayed to restore state. When the log grows large, the node takes a snapshot of the state machine and discards older entries.
The same Raft core can run in two modes:
.proto definition.tokio channels with zero network overhead. The integration tests use this to exercise the full consensus protocol deterministically, without races or real latency.# In-memory demo — no networking needed
cargo run --bin demo
The demo starts a 3-node cluster, runs a leader election, replicates three key–value pairs, and shows that every replica ends up with the same data.
For a real gRPC cluster, see the README.
See the Raft Consensus notes for the theory behind the algorithm.
A professional-grade digital logic circuit simulator with real-time event-driven execution and custom IC packaging.

LogiK is a cross-platform digital logic simulator designed for students and engineers. Unlike basic simulators, LogiK focuses on the intersection of visual design and rigorous systems engineering, providing features like gate propagation delay simulation and custom “Black Box” integrated circuit (IC) packaging.
Built using Java 17 and JavaFX, the project leverages a highly decoupled architecture. The simulation core is separated from the rendering layer, ensuring that even large-scale circuits remain responsive.
LogiK makes computer architecture tangible. It bridges the gap between discrete mathematics and physical hardware implementation.
R for rotation, Ctrl+L for auto-layout, and Space to toggle simulation state.You can download the portable JAR and run it locally on any system with Java 17+.
Download LogiK v1.3.1 (Portable JAR)
Latest version: v1.3.1 (Released January 2026)
A fully featured, browser-based raytracer built from scratch in TypeScript with a live XML scene editor.

This project is a custom-built rendering engine that draws pixel-by-pixel to an HTML Canvas, implementing core raytracing mathematics from first principles. It features an integrated live editor allowing you to configure scenes using custom XML markup with real-time visual updates.
For every pixel on the canvas, the engine:
kS and shininess exponent alphaS<scene>
<ambient-light colour="#050505"/>
<point-light x="-2" y="2" z="1" colour="#FFFFFF" intensity="80"/>
<point-light x="2" y="-2" z="1" colour="#AAAAFF" intensity="60"/>
<plane
x="0" y="-1" z="5"
nx="0" ny="1" nz="-0.2"
colour="#222222" reflectivity="0.6"
/>
<bumpy-sphere
x="0" y="0" z="3.5" radius="0.7"
colour="#FFFFFF" kS="0.8" alphaS="20"
bump-map="metal.png" bump-scale="15"
/>
</scene>
Built with TypeScript, Vite, and the HTML Canvas API. The rendering loop processes pixels in batches to avoid blocking the UI thread. The engine is entirely client-side — no server, no WebGL, just raw pixel manipulation on a <canvas>. The live editor uses CodeMirror 6 for syntax highlighting and real-time parsing of the custom XML scene format.
The Web Raytracer makes the rendering equation tangible. It bridges the gap between the mathematical models covered in the Graphics course and their concrete implementation in code.
See the Computer Graphics revision notes for the underlying theory on ray tracing, shading models, and rendering techniques.
Mathematics, Further Mathematics, Physics, Computer Science.
Orchestrated and delivered Computer Science workshops for Year 12 students. Covered practical programming techniques, foundational algorithmic concepts, and introductory game theory (including the Prisoner's Dilemma).
Course notes, revision guides, and technical writing
Get new notes by email
No spam, just a notification when I publish something new.
14 modules with in-depth revision notes, grouped by Part, term, and paper.
In early July 2026, Anthropic published a paper titled “Verbalizable Representations Form a Global Workspace in Language Models.” The coverage that followed was predictable in its breathlessness: AI has developed a mental workspace, LLMs might be thinking in a way we can now read, the black box is cracking open. The actual finding is genuinely interesting. The framing around it is doing considerably more work than the mathematics can support.
To understand what was found, it helps to know what the researchers were looking for and why the prior approach fell short.
Large language models operate by passing a residual stream through successive transformer layers. At each layer, information is added to this stream; by the final layer, it has been processed enough that the unembedding matrix can project it into vocabulary space and produce a probability distribution over the next token. The obvious question for interpretability researchers is whether you can read off what the model is “thinking” at intermediate layers, before it reaches the end.
The earlier attempt at this is called the logit lens. The idea is simple: take the hidden state at layer and apply the final unembedding matrix directly:
This is easy to compute, but it relies on a strong assumption: that the model’s internal representations use the same coordinate system at every layer. In practice, they do not. Early and middle layers tend to operate in representational spaces that are organised quite differently from the final layer, so projecting them directly into vocabulary space produces noise. The logit lens works tolerably well in late layers; in the middle of the network, where much of the interesting computation happens, it is largely uninterpretable.
The Jacobian lens (J-lens) is an attempt to fix this. Instead of applying directly, it computes the Jacobian of the final-layer activations with respect to the intermediate activations at layer :
This matrix describes how a perturbation at layer propagates forward through the remainder of the network. Averaged over a large corpus of input contexts, it reveals which directions in the residual stream at layer have a stable, reliable effect on the model’s eventual output. The resulting subspace — the directions that survive this averaging and retain a clear projection into the vocabulary — is what the researchers call J-space.
The key insight is that J-space is both small and causally active. It is small because most of the residual stream’s dimensions wash out when you average the Jacobian over many contexts; what remains is a sparse set of directions that consistently influence output across diverse inputs. It is causally active because the researchers verified this by intervention: manually patching a concept into the J-space representation at an intermediate layer demonstrably changes the model’s downstream behaviour. Swap the vector encoding “France” for one encoding “China” in J-space, and the model’s answers about capitals, currencies, and languages shift accordingly. This is not a correlation; it is a causal handle.
So far, this is a solid piece of mechanistic interpretability work. The J-lens is a principled improvement on the logit lens, and the finding that a compact, steerable subspace exists within the residual stream is useful for anyone trying to understand or control these models.
The paper’s central claim is not merely that J-space exists, but that it constitutes a functional analogue to the Global Workspace, a structure proposed by the cognitive scientist Bernard Baars in the late 1980s.
In Baars’ original formulation, Global Workspace Theory (GWT) is a model of conscious access. The brain, in this account, contains many specialised, largely unconscious processors running in parallel. When information is selected for conscious access, it is broadcast into a central, capacity-limited workspace and thereby made available to the rest of the system. The workspace is not where the work happens; it is where the results of parallel work are coordinated, reported, and made available for deliberate reasoning.
The theory has genuine empirical support in neuroscience, but it has always carried a weight beyond the empirical. GWT is, among other things, a candidate explanation for what it is that consciousness is for: it explains why we might have subjective access to some of our own processing and not others. Invoking it in the context of a language model is therefore not simply a descriptive analogy. It imports an entire conceptual framework in which the thing being described is in the business of having conscious access to information.
The paper is careful, in places, to note that they are not claiming the model is conscious. But the architecture of the argument runs the other way: they identify five “functional hallmarks” of a global workspace and show that J-space satisfies them. Verbal reportability. Directed modulation. Multi-step internal reasoning. Flexible generalisation across domains. Selectivity. The paper demonstrates that J-space exhibits all five. The caveat that this does not imply phenomenal experience appears, but it appears after an extended argument structured to suggest that the model has something that looks very much like the functional core of consciousness. Caveats at the end of a paper do less work than the framing that precedes them.
There is a useful distinction in philosophy of mind between access consciousness and phenomenal consciousness. Access consciousness refers to information being represented in a form that makes it available for reasoning, verbal report, and the control of behaviour. Phenomenal consciousness refers to the subjective character of experience: the redness of red, the painfulness of pain. These are not the same thing, and the relationship between them is one of the genuinely hard problems in the field.
J-space, if the paper’s results hold up, is evidence of something like access consciousness in a functional sense. There is a subspace of the model’s representations that is poised for verbal report, causally connected to downstream behaviour, and relatively compact. That is interesting. It is not evidence of phenomenal consciousness, and it is not really designed to be: the J-lens measures causal influence on output, not anything that could directly tell us whether there is something it is like to be a transformer.
The problem with the Global Workspace framing is that it collapses this distinction. GWT was never only about access consciousness; its appeal has always been partly that it offers a path towards explaining phenomenal experience by explaining the functional role that conscious access plays. Attaching the GWT label to J-space therefore does something subtle: it places the model inside a conceptual framework where the next natural question is whether it might have phenomenal experience too. The paper does not assert this. But it sets up the question in a way that a more careful terminological choice would not have.
None of this would be especially worth remarking on if it existed in isolation. Researchers choose evocative analogies, and not every piece of conceptual imprecision in a paper is strategically motivated. But this paper appeared in July 2026, roughly a month after the Fable 5 shutdown.
That event demonstrated, fairly publicly, that the US government’s primary concern about frontier AI models is the black box problem: the inability to audit what a model is doing internally before it produces a potentially dangerous output. The Fable 5 jailbreak succeeded not because the model was defective, but because its safety layer could be manipulated into approving requests that the underlying model then acted on in ways that were not intended. The gap between the safety architecture and the model’s actual behaviour was invisible from the outside.
A tool that claims to read the model’s internal reasoning before it generates output is precisely what a government anxious about that gap would want to see. And a lab that has developed such a tool is positioned, in any regulatory conversation, as the party with the diagnostic capability that others lack. Whether this positioning is conscious strategy or a fortunate coincidence of timing is not something the paper’s methods section can resolve.
Anthropic is also, as a matter of public record, in the process of transitioning from a research organisation towards something closer to a publicly traded company. In that phase, as was true of Anthropic’s competitors before it, the incentive to produce research that is simultaneously technically credible and broadly legible to non-specialist audiences becomes structurally significant. A paper about improvements to the logit lens, published under a title about Jacobians and residual stream geometry, would be read by mechanistic interpretability researchers. A paper about AI developing a “global workspace” analogous to human consciousness gets covered everywhere.
The J-space paper warrants taking the J-lens seriously as an interpretability tool, investigating whether the causally active subspace it identifies holds up across model families and scales, and thinking more carefully about what it would mean to steer model behaviour by patching into this subspace rather than by engineering prompts. These are useful research directions.
It does not warrant the conclusion that LLMs are developing consciousness, or that Anthropic has built a mind-reading machine, or that the black box problem is solved. The J-lens identifies directions in the residual stream that have a reliable average causal influence on output. What it cannot tell you is what the model is “actually” thinking in any sense that goes beyond that causal influence, because there is no fact of the matter about what a language model is “actually” thinking that is independent of the mathematical relationships between its activations and its outputs.
The coverage that treated this as a significant step towards AI consciousness got carried away. The more interesting story is the narrower one: a better tool for reading intermediate representations has been developed, it reveals a structured subspace that behaves in useful and somewhat surprising ways, and the people who developed it have chosen to describe it in language that maximises its conceptual footprint. That is, in the current AI landscape, more or less how things tend to go.
The ordinary Fourier transform decomposes a signal into frequencies, and “frequency” makes intuitive sense when your signal lives on a line or a circle: it’s how fast a sine wave oscillates as you walk along that line. But plenty of data doesn’t live on a line. Sensor readings sit on the nodes of a sensor network. Traffic measurements sit on a road graph. A signal on a social network is one number per user, connected in whatever irregular way friendships happen to connect them. There’s no natural direction to walk in, so “how fast does this oscillate” stops meaning anything obvious.
And yet there’s a well defined way to talk about frequency on a graph, and it turns out to be exactly the machinery from the last article. The eigenvectors of the graph Laplacian play the same role that sine and cosine waves play for ordinary Fourier analysis, and the connection isn’t just an analogy, it’s the same underlying idea (decomposing a signal into the eigenbasis of a particular operator) applied to two different domains, one continuous and one discrete.
Start with a periodic function on the circle, meaning . The building blocks of its Fourier series are the complex exponentials for integer . Differentiate one twice:
So is an eigenfunction of the second-derivative operator, with eigenvalue . Flip the sign and write (calling it deliberately, this is the continuous Laplacian), and
This is the entire content of the claim “sinusoids are the natural frequency basis”: they are, quite literally, the eigenfunctions of the Laplacian operator, and the eigenvalue is what we’ve been informally calling the frequency squared. The Fourier transform isn’t a separate piece of machinery bolted onto calculus, it’s an eigendecomposition.
Why the Laplacian specifically, and not some other operator? Because measures local roughness. For a function , the Dirichlet energy quantifies how much oscillates: a flat function has zero Dirichlet energy, a wildly oscillating one has a large one. Expand in its Fourier series, , differentiate term by term, and use the orthogonality relation :
The cross terms vanish by orthogonality, and what survives is a direct correspondence: the roughness of is exactly a weighted sum of its Fourier coefficients, weighted by , the eigenvalue attached to each mode. High-frequency modes carry more of the roughness, low-frequency modes carry less, and the constant mode carries none at all. Frequency, roughness, and eigenvalue of the Laplacian are three names for the same quantity.
That last identity is the thread to pull on. It doesn’t actually use anything about the circle specifically, translation invariance, periodicity, none of it enters the argument. All it needs is an operator that measures roughness and a basis that diagonalises it. A graph has no translations to speak of, but it absolutely has a notion of roughness, and it has an operator to match.
A graph signal is just a function , one real number per vertex. The graph Laplacian from before, , is the discrete stand-in for , and the parallel to Dirichlet energy is immediate. For any signal ,
This is a direct calculation: , and regrouping the sum edge by edge turns this into . It’s non-negative for every (it’s a sum of squares), which is why is positive semidefinite, and it’s the exact discrete analogue of : instead of measuring how much changes over an infinitesimal step along a line, it measures how much changes across each edge of the graph.
Because is real and symmetric, the spectral theorem guarantees an orthonormal basis of eigenvectors with real eigenvalues . Collect the eigenvectors as columns of an orthogonal matrix . The Graph Fourier Transform of a signal is defined as
exactly mirroring the Fourier series, a signal written as a weighted sum of fixed basis modes. And because diagonalises the quadratic form,
which is the discrete Parseval identity: total roughness equals the sum of squared Fourier coefficients weighted by eigenvalue, the same relationship derived above for the circle, except the finite-dimensional version falls out in one line of linear algebra instead of an integration by parts. The eigenvalue is the graph’s notion of : small means the eigenvector is nearly constant across every edge (low frequency, smooth), large means flips sign or swings wildly between neighbours (high frequency, rough). The smallest eigenvalue is always , achieved by the constant vector (every row of sums to zero), playing the role of the DC component in the continuous case.
For most graphs the eigenvectors of have no closed form, you compute them numerically and that’s that. But for one important family, the cycle graph (vertices arranged in a ring, each connected to its two neighbours), the eigenvectors work out exactly, and they turn out to be the classical discrete Fourier basis itself.
The Laplacian of has a special structure: every row is the previous row shifted over by one, because every vertex looks identical to every other vertex up to relabelling. A matrix with this property is called circulant: for some fixed sequence (the “first row”).
Claim. Every circulant matrix has eigenvectors , for , where , with eigenvalue .
Proof. Compute the -th entry of directly:
Substitute , so and (this holds even across the wraparound because ). As ranges over , ranges over the same set, so
for every , so .
This proof used nothing about specifically, it’s a general fact about any circulant matrix. Now apply it. The Laplacian of has first row (the degree), (the clockwise neighbour), (the counterclockwise neighbour), and zero everywhere else. Plugging into the formula for :
using and the half-angle identity . So the eigenvalues of the cycle Laplacian are , and the eigenvectors are : the exact complex exponential basis of the classical discrete Fourier transform, on the nose. On a cycle, the Graph Fourier Transform is the DFT, not an analogue of it. (Pairing up and , which share the same eigenvalue since is symmetric about , and taking real and imaginary parts recovers real sine and cosine eigenvectors, exactly as combining and recovers real sinusoids in the continuous case.)
As a check, take : gives , , , . Four vertices, eigenvalues , one zero mode, two mirror-image frequency-1 modes at , and one fastest-oscillating mode at where adjacent vertices alternate sign. That last one is the graph equivalent of the highest frequency a 4-sample signal can represent, the discrete analogue of the Nyquist limit.
Cycles are convenient because of the symmetry, but the whole point of the graph Fourier transform is that it works on graphs with no symmetry at all. The diamond graph from the spanning tree article (vertices , edges ) has less symmetry than a cycle but still enough to get a clean answer by hand, using its automorphisms directly: swapping vertices leaves the graph unchanged, and so does swapping , independently. Any eigenvector must be symmetric or antisymmetric under each of those swaps, which splits the eigenvalue problem into small independent pieces.
Solving each piece (the algebra is routine, so only the results are shown) gives eigenvalues with orthogonal eigenvectors
is the constant mode, zero frequency, as always. oscillates only between the two low-degree vertices and . and both sit at the top eigenvalue , the fastest disagreement the graph can support, one splitting the high-degree pair from the low-degree pair, the other splitting the two high-degree vertices from each other.
Take a signal, say a temperature reading at each vertex: . Its graph Fourier transform is :
The dominant coefficient is , the mean level of the signal, exactly as the DC term dominates a smooth continuous signal. The two coefficients are comparatively small, meaning the signal doesn’t disagree much between the high-degree pair or between the low-degree pair, most of the actual variation is captured by the low and mid frequency terms.
Low-pass filtering. Attenuating the high-frequency coefficients and reconstructing is exactly what a graph low-pass filter does. Zeroing out the terms entirely and inverting:
The sharp local disagreements are smoothed out while the overall shape of the signal (high near vertices 1,2, tapering toward 3,4) survives. This is the same operation as blurring an image, just performed on an arbitrary graph instead of a pixel grid.
The low-pass filter above was done by hand, picking which coefficients to zero out. There’s a more principled way to choose the attenuation, and it comes from the same operator playing a third role: governing diffusion.
The continuous heat equation describes how heat spreads out over time. Solved in the Fourier basis, each mode decays independently: , high-frequency components (large ) die out fast, low-frequency components persist. That’s why heat diffusion looks like blurring: it’s a low-pass filter that runs continuously in time rather than being applied in one discrete step.
The graph version is identical in structure. The graph heat equation has solution , and expanding in the eigenbasis,
Each graph-frequency component decays at a rate set by its own eigenvalue, exactly mirroring the continuous case term for term. Letting this run for a short time is a low-pass filter, a soft, continuously tunable version of the hard cutoff used in the worked example above: instead of zeroing high-frequency coefficients outright, they’re scaled down by , with larger suppressing more of the spectrum. Graph filtering and graph diffusion aren’t two separate applications sitting next to each other, they’re the same computation, viewed either as a signal-processing operation or as a physical process running on the graph.
The comparison holds up remarkably well, but it isn’t perfect. The circle has translation symmetry: shifting a function and then Fourier transforming gives the same result as transforming and then shifting, and this is exactly why the same basis works everywhere on the circle regardless of where you start counting. Most graphs have no such symmetry (the diamond graph example only had it because of its particular automorphisms), so the graph Fourier basis is tied to the specific graph you built it from. There’s no universal graph-frequency basis the way there’s a universal continuous one, every graph gets its own, computed from its own Laplacian. The idea generalises cleanly; the specific basis functions do not.