# Shine documentation Shine designs and simulates photonic circuits: free-space optics on a bench, waveguide components on a chip, and the electronics that drive and read them. It runs in the browser at [shine.lupyd.com/app](https://shine.lupyd.com/app), as a desktop app on Linux, Windows and macOS, as a Rust library, and as an MCP server for AI assistants. ![The editor with the 4-channel WDM optical link example open](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/wdm-link.webp) ## Guides | Guide | What it covers | |---|---| | [Getting started](https://shine.lupyd.com/docs/getting-started/) | Install or open Shine, tour the window, run your first simulation | | [Editing](https://shine.lupyd.com/docs/editor/) | Placing, selecting, moving, the add menu, the parts browser, keyboard and mouse | | [Parts reference](https://shine.lupyd.com/docs/parts/) | Every part and block: what it does, its ports and parameters | | [Wiring light and signals](https://shine.lupyd.com/docs/wiring/) | Waveguides, beams, signal wires, and converting between light and signals | | [Components](https://shine.lupyd.com/docs/components/) | Turning a layout into a reusable box, ports and pins, trying a component on its own | | [Electronics and the clock](https://shine.lupyd.com/docs/electronics/) | Number inputs, displays, registers, the clock, and function-cell expressions | | [Examples](https://shine.lupyd.com/docs/examples/) | The ready-made circuits and what each one teaches | | [How the simulation works](https://shine.lupyd.com/docs/simulation/) | What is solved exactly, what is modelled simply, and the limits | | [Projects and files](https://shine.lupyd.com/docs/files/) | The `.shine` format, the browser and desktop builds, logging | | [AI assistants (MCP)](https://shine.lupyd.com/docs/ai/) | Using Shine from Claude and other MCP clients; notes for LLMs | | [Rust library](https://shine.lupyd.com/docs/library/) | Using the `photonics` crate without the editor | | [Development](https://shine.lupyd.com/docs/development/) | Building, testing, keeping these docs current, releases | | [Roadmap](https://shine.lupyd.com/docs/roadmap/) | What's being built for 0.1.0 and what comes after | | [Changelog](https://shine.lupyd.com/docs/changelog/) | What changed in each version | On the website, **Search** at the top of every docs page (Ctrl+K) searches all of them. For language models, the whole documentation is also served as plain text at [`/llms.txt`](https://shine.lupyd.com/llms.txt) (an index) and [`/llms-full.txt`](https://shine.lupyd.com/llms-full.txt) (everything in one file). --- # Getting started ## Open or install - **Browser:** open [shine.lupyd.com/app](https://shine.lupyd.com/app). Nothing to install; your work stays in the page until you download it (File → Save). - **Desktop (faster):** download for your system from [shine.lupyd.com](https://shine.lupyd.com/#download) or the [GitHub releases](https://github.com/ash-hashtag/shine/releases): Linux (x86-64, ARM64), Windows (x86-64) or macOS (Apple silicon, Intel). Unpack it and run `shine` (`shine.exe` on Windows). The desktop app uses every CPU core and your processor's vector instructions, where the browser runs on one core, and it saves files directly. The archive also contains `shine-mcp`, the server for AI assistants (see [AI assistants](https://shine.lupyd.com/docs/ai/)). - **From source:** `cargo run --release` (Rust 1.88 or newer). ## The window ![The editor window](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/wdm-link.webp) - **Menu bar** (top): File, Edit, View, Component, Help, and the **Simulate / Stop simulation** switch on the right. Simulation is on by default and updates as you edit. - **Tabs**: one per open document. A project can hold many layouts; components are layouts too, and open in their own tab. - **Library** (left): parts in folders, a search box, and ▦ to open the [parts browser](https://shine.lupyd.com/docs/editor/#the-parts-browser). Below it, the **Project** list of every document. - **Canvas** (middle): the grid where you place parts and wire them. - **Inspector** (right): the selected item's settings and readings, simulation settings, the clock, displays and detector readouts. - **Status bar** (bottom): zoom, the cell under the cursor, hints and messages. ## Your first circuit: a laser and a detector 1. Drag **Laser** from *Light & detection* onto the grid. It points right. 2. Drag **Detector** to a cell a few cells to the right of the laser, on the same row. 3. A beam appears and the detector reads `1.000`, the laser's power. 4. Drag a **Beam Splitter** between them. The beam splits; the detector now reads `0.500` and the reflected half leaves the splitter at 90°. 5. Press **R** with the splitter selected to rotate it 45°, or **G** to move it. Hover a beam to see its wavelength, power and phase. ## Open an example File → Examples… lists ready-made circuits. Start with these: - **4-channel WDM optical link**: what photonic chips do in data centres today. Click the input bits and they arrive at the receiver. - **8-bit photonic adder**: a teaching model of logic with light. - **Mach–Zehnder interferometer**: interference on a free-space bench. See [Examples](https://shine.lupyd.com/docs/examples/) for what each one shows. ## Next - [Editing](https://shine.lupyd.com/docs/editor/) explains the mouse and keyboard controls. - [Wiring light and signals](https://shine.lupyd.com/docs/wiring/) explains the two kinds of connection. --- # Editing The controls follow Blender's conventions. Press **F1** (or **?**) in the app for the full list. ## The grid Everything sits on a square grid. Free-space parts (lasers, mirrors, detectors…) take one cell and are drawn as themselves. Built-in blocks and components are **boxes** that take several cells: light ports on their left and right edges, signal terminals on the top and bottom edges. The **cell size** (Inspector → Simulation, 10 µm by default) sets the physical distance light travels between cells, which matters for interference. ## Adding parts There are four ways, all placing into the cell you point at: - **Drag** a row from the library onto the grid. - **Click** a library row, then click the grid (repeat to place more; Esc stops). - **Shift+A** (or right-click empty space) opens the **add menu** at the cursor. - The **parts browser** (▦ next to the library search). ### The library ![The library folders](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/library.webp) Parts are grouped into folders: *Light & detection*, *Free-space optics*, *Electronics & I/O*, *Example components*, *My components*, *Waveguide blocks* and *Component ports*. Click a folder to open or close it. Typing in the search box shows matching parts from every folder in one list. ### The add menu ![The Shift+A menu with the Electronics & I/O category open](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/add-menu.webp) Hover a category to open its parts beside it, then click one. Or just start typing: the menu turns into a search, and **Enter** adds the first match. Scrolling over the menu scrolls the menu, not the grid. **Esc** or a click on the grid closes it. ### The parts browser ![The Parts & components browser](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/browser.webp) The browser shows every part as a tile with its description, folder by folder. Click a tile and then click the grid, or drag the tile onto the grid. **+ New component** starts a [component](https://shine.lupyd.com/docs/components/). ## Selecting, moving, deleting - **Click** selects; **Shift+click** adds or removes; **drag on empty space** draws a selection box. Clicking a waveguide or signal wire selects it. - **Drag** a part or box to move it, or press **G** and move the mouse (**X** / **Y** lock the axis, click or Enter confirms, right-click or Esc cancels). Parts can't be dropped onto occupied cells. - **R** / **Shift+R** rotate lasers by 90° and mirrors and splitters by 45°. Double-clicking a part rotates it too. - **Shift+D** duplicates the selection (with the wires between the copies) and starts moving it. - **X** or **Delete** removes the selection, or the selected wire. - **Ctrl+Z** / **Ctrl+Shift+Z** undo and redo. ## Navigating - **Wheel** or **pinch** zooms at the cursor; **trackpad scrolling** and **middle-drag** pan; **Ctrl+wheel** / **Shift+wheel** pan sideways / up and down; **Ctrl+middle-drag** zooms. - **Home** frames everything; **.** (period) frames the selection. ## Preferences **Edit → Preferences…** sets how Shine looks and behaves, and is remembered between sessions (a settings file on desktop, the browser's storage on the web): | Setting | Options | |---|---| | Theme | System, light or dark | | Grid, port names | Show or hide the grid and the names on component boxes | | Light colours | Real colours where visible (infrared and ultraviolet in distinct false colours), a distinct colour per wavelength, or one colour; use lasers' own colours | | Channels | Draw each wavelength on a waveguide or beam as its own strand | | Beams, readings | Direction arrows; detector powers on the canvas | | New lasers | Their wavelength, typed or from presets | | Simulation | Live simulation for new documents; clock speed | ## Keyboard and mouse reference | Keys | Action | |---|---| | Middle drag / Shift+Middle drag | Pan | | Ctrl+Middle drag | Zoom | | Wheel / pinch | Zoom at cursor | | Trackpad scroll | Pan | | Home | Frame all | | . (period) | Frame selected | | Left click | Select | | Shift+Left click | Extend / toggle selection | | Left drag on empty space | Box select (Shift extends) | | Left drag on component | Move it | | Double click | Rotate a part / open a component | | Click a number's bit | Toggle that bit | | A / Alt+A / Ctrl+I | Select all / none / invert | | G | Grab (move); X / Y constrain axis | | Enter / Left click | Confirm grab | | Esc / Right click | Cancel grab | | R / Shift+R | Rotate clockwise / counter-clockwise | | Shift+D | Duplicate and move | | X / Delete | Delete selected | | Shift+A / Right click | Add menu / context menu | | Tab / Double click | Open a component's definition in its own tab | | Drag from a port or terminal | Wire it: light ports take waveguides, square terminals take signals | | Ctrl+Z / Ctrl+Shift+Z | Undo / redo | | Space | Toggle simulation | | T / N | Toggle toolbar / properties | | Ctrl+N / Ctrl+O / Ctrl+S | New / open / save | | F1 / ? | This help | --- # Parts reference Select a part to edit its parameters in the inspector. Hover any part on the grid for a short description and its current readings. Ports are named so you can tell them apart: **light ports** are round (left edge = in, right edge = out); **signal terminals** are square (top edge = in, bottom edge = out). ## Light & detection | Part | What it does | Signal terminals | |---|---|---| | **Laser** | Source with amplitude, phase, wavelength (with presets from violet to the telecom C band), a *source* and an optional colour. Points right; R rotates it in 90° steps. *Own* source (the default): its light never interferes with other lasers, as with real separate lasers. *Shared A–H*: lasers on the same letter are phase-locked, like light split from one laser, and interfere; the letter shows next to the laser. | in: *amplitude*, multiplies the amplitude (0 switches it off, 1 leaves it on) | | **Detector** | Photodiode: absorbs light from any direction and reads its power. | out: *power* | | **Threshold detector** | A detector whose *bit* option is on: outputs 1 when the power reaches the threshold, else 0. Any detector can be switched between the two in the inspector. | out: *power* (as 0/1) | ## Free-space optics | Part | What it does | Signal terminals | |---|---|---| | **Beam Splitter** | Partially reflecting mirror; reflectivity sets the split. Rotates in 45° steps. | — | | **Mirror** | Reflects light; the dark side absorbs. Rotates in 45° steps. | — | | **Phase Shifter** | Adds a phase to light passing through. | in: *phase*, adds π per unit (1 flips an interferometer) | | **Attenuator** | Passes a fraction of the power (transmission 0–1). | in: *transmission*, multiplies it | Older projects can contain a *Lens*; it is no longer in the library because a ray-on-grid model can't focus, so it only passes light through. ## Electronics & I/O | Block | What it does | Terminals | |---|---|---| | **Number input** | A number you set (1–16 bits), sent out as bits, most significant first. Click a lamp to toggle that bit. | out: b(n-1)…b0 | | **Display** | Shows the bits on its inputs, as lamps and as a decimal number. | in: b(n-1)…b0 | | **Register** | Memory. On each clock tick it stores its *d* inputs when *write* is high or unconnected, and holds them on its *q* outputs. | in: d…, write; out: q… | | **Function cell** | Computes outputs from expressions over its inputs. Can read received light and emit light. See [Electronics](https://shine.lupyd.com/docs/electronics/#function-cells). | in: in1…; out: out1…; light in: p1…; light out: light1… | ## Example components Ready-made [components](https://shine.lupyd.com/docs/components/) built from the parts above. Placing one adds a copy to your project that you can open with Tab and change. | Component | Ports | |---|---| | **Full adder** | signals in a, b, cin; out sum, cout. A teaching model: see [Examples](https://shine.lupyd.com/docs/examples/#8-bit-photonic-adder). | | **Mach–Zehnder interferometer** | light in1; out out1, out2. The phase shifter inside picks the output. | | **Fabry–Pérot filter** | light in, out. Passes only resonant wavelengths. | | **1→4 splitter** | light in; four outputs with a quarter of the power each. | ## My components Every layout you save as a component appears here. See [Components](https://shine.lupyd.com/docs/components/). ## Waveguide blocks On-chip building blocks, drawn as boxes and connected with waveguides. | Block | Light ports | Parameters | |---|---|---| | **Directional coupler** | 2 in, 2 out (bar and cross) | coupling (power to the cross port) | | **Phase shifter** | 1 in, 1 out | phase | | **Attenuator** | 1 in, 1 out | loss in dB | | **Delay line** | 1 in, 1 out | length (µm), effective index; phase depends on wavelength | | **MZI switch** | 2 in, 2 out | θ (internal: π = bar, 0 = cross), φ (input phase) | | **Y-splitter** | 1 in, 2 out | — (equal split) | | **Y-combiner** | 2 in, 1 out | — (only the in-phase part is guided) | | **Crossing** | 2 in, 2 out | — (no coupling) | | **Ring resonator** | in → through | coupling, length, effective index, loss (dB/cm) | | **Add–drop ring** | in, add → through, drop | coupling, length, effective index, loss; resonant wavelengths go from in to drop (and from add to through) | | **Splitter tree** | 1 in, 2^levels out | levels | | **Switch tree** | 1 in, 2^levels out | levels, address (which output gets the light) | | **Optical DFT** | n in, n out | modes; an MZI mesh computing the discrete Fourier transform | A ring of length L and effective index n resonates at wavelengths λ where n·L = m·λ for a whole number m. The [WDM link](https://shine.lupyd.com/docs/examples/#4-channel-wdm-optical-link) uses L = 20·λ / 2.4 to put each ring on its own channel. ## Component ports Used inside a layout to give it a boundary when it becomes a [component](https://shine.lupyd.com/docs/components/). | Part | Becomes on the box | |---|---| | **Input port** | a light input on the left edge (ordered top to bottom) | | **Output port** | a light output on the right edge | | **Signal in** | a signal terminal on the top edge (ordered top to bottom, then left to right). In the layout's own tab, click it to switch it between 0 and 1. | | **Signal out** | a signal terminal on the bottom edge | Name a port in the inspector; the name shows on the box. --- # Wiring light and signals A circuit carries two different things, and Shine keeps them apart: | | Light | Signals | |---|---|---| | What it is | an optical field: amplitude, phase, wavelength | an electrical value: usually 0 or 1, sometimes a number | | Drawn as | beams (free space) and thick coloured waveguides | thin right-angled wires; orange = 1, grey = 0, blue = other values | | Ports | round, on the left (in) and right (out) of boxes | square, on the top (in) and bottom (out) | | Fan-out | one waveguide per port | one output can drive many inputs | ## Beams Free-space parts talk through beams. A laser shoots a beam along its direction; mirrors, splitters and other parts redirect it; detectors and the backs of mirrors absorb it. Beams travel along rows, columns and 45° diagonals. A beam also connects to boxes: a beam that reaches the left edge of a box on the row of a light input enters that input, and light leaving an unconnected output of a box goes out of its right edge as a beam. Light hitting a box anywhere else is absorbed. ## Waveguides Drag from a box's light output (round, right edge) to another box's light input (round, left edge). The waveguide is drawn as a curve, coloured by the light it carries. Its length follows the drawing, so moving boxes changes the phase. Each port takes one waveguide; a new one replaces the old. Click a waveguide and press X to delete it. ## Signal wires Drag from a square output terminal (bottom edge) to a square input (top edge), or the other way round. An input has one driver; an output can drive many inputs. Wires are routed with right angles, and wires between the same rows get separate lanes so buses stay readable. Parts with signal terminals: - **Laser** (input on top): the amplitude is multiplied by the signal. - **Phase Shifter** (input on top): adds π per unit. - **Attenuator** (input on top): multiplies the transmission. - **Detector** (output below): the received power, or 0/1 with *bit* on. - the electronics blocks, function cells, and components' **Signal in/out** pins. ## Converting between light and signals Light and signals never connect directly. If you drag a wire from one to the other, the wire turns amber while you hover, and dropping it opens a small offer that explains what is needed, with a button that inserts and wires it: - **Light → signal: a detector.** Shine places a detector in the beam leaving that light output and wires its output to the signal input. For displays and registers it switches the detector to 0/1 output. - **Signal → light: a laser.** Shine places a laser in front of that light input and wires the signal to its amplitude, so 1 turns it on and 0 off. If the cell it needs is taken, it says which cell to clear. In real chips these are a photodiode with a receiver circuit, and a modulator on a laser. --- # Components Any layout can become a **component**: a box you can place in other layouts, as many times as you like. Edits to the component's layout update every place it's used. ![A free-space Mach–Zehnder saved as a component and used twice](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/components.webp) ## Making one - **+ New component** (library → *My components*, the parts browser, or the **+** tab button) creates a component with one light input and output and one signal input and output, and asks for its name, initials and description. - Or turn any open layout into one: **Component → Save as component…** The box shows the **initials**; hovering it shows the **description** and its ports. ## Ports and pins What the box exposes comes from port parts inside the layout: | Inside the layout | On the box | Order | |---|---|---| | Input port | light input, left edge | top to bottom | | Output port | light output, right edge | top to bottom | | Signal in | signal terminal, top edge | top to bottom, then left to right | | Signal out | signal terminal, bottom edge | top to bottom, then left to right | Name each port in the inspector (for example `a`, `b`, `cin`); the names show on the box and in tooltips. Without names they are `in1…`, `out1…`, `s1…`, `q1…`. An Input port injects light travelling in its arrow's direction; an Output port reads the light that reaches it. Signal in outputs the value arriving at the box; Signal out takes a value and passes it out of the box. ## Opening a component Select a box and press **Tab**, or double-click it, to open its layout in a tab. The inspector shows where it's used. You can't place a component inside itself, directly or through other components. ## Trying a component on its own In a component's own tab nothing drives its Signal in pins, so they read 0. **Click a Signal in pin** to switch it between 0 and 1; a lamp next to it shows the value and the simulation follows. This lets you test, for example, the Full adder's truth table without building anything around it. These test values aren't saved and don't affect the component where it's used. ![The Full adder component opened in its own tab](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/full-adder.webp) ## Managing components In the **Project** list (left, below the library), right-click a document to open, rename, edit its component details, or delete it. **Component → Stop sharing as component** turns it back into a plain layout. A component that is still used somewhere can't be deleted or unshared until its boxes are removed. --- # Electronics and the clock Shine's electronics are deliberately small: enough to drive optics and read it back, not a general logic simulator. Light does the optical work; detectors, registers and function cells provide decisions and memory. ## Number inputs and displays A **Number input** sends a number as bits, most significant first (leftmost terminal). Click its lamps to toggle bits, or set the value in the inspector. A **Display** shows the bits on its inputs as lamps and as a decimal number; the inspector lists every display's value under *Displays*. ## Registers and the clock A **Register** has *d* inputs, a *write* input and *q* outputs. Its outputs hold the stored value. On each **clock tick** it stores its *d* inputs, unless *write* is connected and 0. The clock lives in the inspector under *Simulation*: - **Step** advances one tick. - **Run** ticks twice a second until you press **Pause**. - **Reset** clears every register and the tick count. Registers make feedback possible. The [Photonic accumulator](https://shine.lupyd.com/docs/examples/#photonic-accumulator) adds a step to a register on every tick. ## Threshold detectors A detector outputs the power it receives. Tick **bit** in its inspector to give it a comparator: it outputs 1 when the power reaches the threshold (0.5 by default), else 0. This is where an analog optical result becomes a digital decision. ## Function cells A **Function cell** computes its outputs from expressions you write. Use it for logic you don't want to build from parts, to model a receiver circuit, or to drive light from a calculation. In the inspector: - **Signal inputs**: how many `in1`, `in2`, … terminals it has. - **Light inputs**: how many light ports it has on the left; `p1`, `p2`, … are the powers received there. - **Signal outputs**: one expression per `out` terminal. - **Light outputs**: one expression per light output; the value is the field **amplitude** (power = amplitude²) at the cell's wavelength. A red `!` next to an expression means it doesn't parse; hover it for the reason. ### Expression reference | | | |---|---| | Variables | `in1`, `in2`, … signals; `p1`, `p2`, … received light power; `t` clock ticks | | Numbers | `3`, `0.5`, `1e-3` | | Arithmetic | `+ - * / %`, `**` (power), unary `-` | | Bits | `<< >> & \| ^ ~` on whole numbers | | Comparison and logic | `< <= > >= == !=`, `&&`, `\|\|`, `!`; true is 1, false is 0 | | Choice | `cond ? a : b` | | Functions | `abs sqrt floor ceil round exp ln sin cos`, `pow(x, y)`, `bit(x, k)` (bit k of x), `clamp(x, lo, hi)`, `min(…)`, `max(…)`, `sum(…)`, `argmax(…)` (0-based index of the largest) | Examples: - XOR of two bits: `in1 ^ in2` - Majority of three: `in1 + in2 + in3 >= 2` - A light level from a 2-bit number: `sqrt(in1 * 2 + in2) / 2` - Which of four detectors is brightest: `argmax(p1, p2, p3, p4)` ## How it settles Each time something changes, Shine evaluates the electronics from the detector readings, re-solves the light with the new drive values, and repeats until nothing changes (usually 2–5 rounds; the inspector shows how many). A loop that keeps flipping, such as a NOT gate feeding itself, is reported as *did not settle*. See [How the simulation works](https://shine.lupyd.com/docs/simulation/). --- # Examples Open them with **File → Examples…**, or link straight to one: `https://shine.lupyd.com/app/?example=`. ## Circuits ### 4-channel WDM optical link ![The WDM link sending 11](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/wdm-link.webp) What photonic chips do in data centres today. Four bits switch lasers at 1530, 1540, 1550 and 1560 nm. Add–drop rings tuned to each wavelength put them on one waveguide; identical rings at the receiver drop each colour onto a threshold detector, and a display shows the number received. Real transceivers run each wavelength at 25–100 Gb/s. Try: click the input bits; or select the first receiver ring and change its length slightly. It stops matching its laser, and that bit is lost. ### 8-bit photonic adder ![The 8-bit adder showing 100 + 55 = 155](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/adder.webp) Eight Full adder components with chained carries add two 8-bit numbers. Each full adder computes its sum bit by interference (the input bits drive phase shifters in a Mach–Zehnder) and its carry with a threshold detector. How realistic: an **electro-optic teaching model**. Computing XOR by interference is a real technique, but here every stage converts back to electronics; research chips keep the carry in light and detect only at the end. In practice photonics is used for data links and linear algebra, not digital arithmetic. Press Tab on a full adder to see its optics, and click its pins to try it alone. ### Photonic accumulator ![The accumulator](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/accumulator.webp) A register feeds eight full adders whose sums feed back into the register: each clock tick adds the step (3) to the running total, wrapping at 256. Press **Step** or **Run**. ### Cascaded interferometers A free-space Mach–Zehnder saved as a component and used twice: a beam enters the first box, and a waveguide joins it to the second. ## Free-space optics | Example | Shows | |---|---| | Mach–Zehnder interferometer | A beam split into two arms and recombined; the phase shifter steers all the light between two detectors. The basic switch and modulator of photonic chips. | | Michelson interferometer | Both arms fold back on mirrors, so light crosses the phase shifter twice: π/2 already darkens the detector. Used for precise length measurement. | | Fabry–Pérot cavity | Two 90% mirrors form a resonator that transmits everything on resonance; nudge the wavelength or cell size to fall off it. | | Coherent XOR gate | Two lasers on shared source A carry bits in their phase (0 or π); after a π/2 bias and a splitter, one detector reads a XOR b and the other XNOR. Put one laser on its own source and the interference disappears. | | 1→4 fan-out tree | Three 50:50 splitters share one laser equally over four detectors. | ![The free-space Mach–Zehnder](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/free-space.webp) ## Chip circuits The *Chip circuits* tab explores waveguide circuits with sliders and a spectrum or power plot: | Example | Shows | |---|---| | Ring resonator (notch filter) | A ring beside a bus removes the wavelengths that fit around it; critical coupling makes the notch go to zero. | | Add–drop ring filter | A ring between two waveguides moves its resonant wavelength to the drop port. | | Interleaver (unbalanced MZI) | Unequal arms send alternate wavelengths to alternate outputs. | | WDM demultiplexer | Rings tuned to four channels separate them onto four outputs. | | MZI switch tree (router) | Cascaded MZI switches route one input to any of several outputs. | | Optical Fourier transform | An MZI mesh programmed with the DFT matrix focuses a tone into one output. | | Matrix–vector multiplier | Two MZI meshes and attenuators multiply a vector by any matrix (SVD). | | 1×N splitter tree | Y-splitters share one input equally over N outputs. | ![The optical Fourier transform chip example](https://raw.githubusercontent.com/ash-hashtag/shine/main/docs/img/chips.webp) --- # How the simulation works ## Light: exact, linear, per wavelength Every optical part is a **scattering matrix**: it maps the complex field (amplitude and phase) on its inputs to the field on its outputs. Shine connects these into one circuit and solves the steady state **exactly** as a linear system, once per laser wavelength. - **Interference** comes out right because fields add, not powers. - **Loops and resonances** (rings, cavities, a Fabry–Pérot) are solved directly, not by bouncing light a fixed number of times. - **Phase from distance**: a beam crossing one cell picks up the phase of one cell size (Inspector → Simulation → Cell size). Waveguides use their drawn length with an effective index of 2.4. - **Coherence.** Only lasers on the same *shared source* (A–H) interfere, like light split from one laser. Independent lasers (the default) and different wavelengths are solved separately and their powers added, as with real separate lasers. All independent sources at one wavelength are solved together in one SIMD pass, so this stays fast. Built-in laser blocks and function-cell light share one source. Fields are normalised so |E|² is power: a laser of amplitude 1 delivers power 1. ## Electronics: settled against the light Electronics are evaluated from the detector readings, the light is re-solved with the new drive values (laser amplitudes, phase shifts, transmissions, function-cell light), and this repeats until nothing changes. Registers only change on a clock tick, so feedback through a register is well defined. A combinational loop that keeps flipping is reported as an error instead of guessed. ## Components A component box is simulated from its layout every time, nested as deep as you like; there's no cached approximation. A beam enters a box only on the row of a light input, travelling right; unconnected light outputs leave the box as beams to the right. ## Colours Colours are only a display choice (Edit → Preferences): visible wavelengths can show their real colour, infrared and ultraviolet get distinct false colours, or every wavelength gets its own colour, or all light one colour; any laser can have its own colour. A waveguide carrying several wavelengths is drawn as one strand per channel, and the inspector lists which colour is which wavelength. ## What is simplified Read results with these in mind: - **Ideal parts.** Splitters, mirrors and couplers are lossless unless you add loss; detectors are noiseless and instantaneous; lasers have no linewidth or noise. - **Steady state only.** No time dynamics: no pulse shapes, rise times, bandwidth or propagation delay. "Clock ticks" are logical steps, not a time scale. - **Grid optics.** Beams are rays on a grid in 8 directions; there is no diffraction, beam width or polarisation. The lens passes light straight through. - **Waveguides** have a fixed effective index (2.4) and no dispersion or bend loss. - **Electronics** are idealised logic and arithmetic, not circuits: no voltages, thresholds' noise margins, or timing. So Shine is right about *interference, routing, resonance and wavelength behaviour* of a design, and is a good place to learn and prototype ideas. It is not a substitute for a device-level (FDTD, mode solver) or circuit-level (SPICE, time-domain) simulator when designing a chip for fabrication. ## Performance Circuits compile per wavelength into a flat program: parts on straight paths become SIMD multiply-adds over batches of inputs, and each feedback loop gets an LU factorisation computed once. Sweeps over wavelength run in parallel on desktop builds. --- # Projects and files ## Projects A project holds every document you have open in tabs: layouts and components. **File → Save** writes the whole project to one `.shine` file; **File → Open** reads it back. - **Desktop:** Save asks for a path the first time, then saves in place (**Save as…** to choose again). Ctrl+S, Ctrl+O and Ctrl+N work too. - **Browser:** Save downloads the file; Open uploads one. Nothing is stored on a server. ## The `.shine` format A compact binary file: the 4 bytes `SHNE`, a 2-byte format version (little-endian), then the project encoded with [postcard](https://docs.rs/postcard). It is about five times smaller than the same project as JSON. Files from older versions are converted when opened: format 1's separate schematics become layouts of boxes, and format 2's lasers (which all interfered with each other) go onto shared source A so they simulate as before. A file from a *newer* Shine than yours is refused with a message to update. Preferences are not part of a project; they're saved with the app. JSON layouts from early versions can still be opened. ## Desktop app Releases contain `shine` (the editor) and `shine-mcp` (the [AI server](https://shine.lupyd.com/docs/ai/)). Command line options: | Option | Effect | |---|---| | `--log-file ` or `SHINE_LOG_FILE` | also write the log to a file (appended) | | `--log-level `, `SHINE_LOG` or `RUST_LOG` | log filter, e.g. `debug` or `shine=trace` | Logs always go to stderr. The Windows release opens no console window, so use `--log-file` there. ## Browser app The browser build is the same editor compiled to WebAssembly. Links can open things directly: | Query | Effect | |---|---| | `?example=` | open an example, e.g. `?example=4-channel-wdm-optical-link` | | `?tab=` | switch to the document with that name, e.g. `&tab=full-adder` | | `?examples=demos\|layouts\|chips` | open the examples window on that tab | | `?chip=` | pick a chip-circuit example, e.g. `?examples=chips&chip=wdm-demultiplexer` | | `?renderer=webgpu` | use WebGPU instead of WebGL2 | Names are written in lower case with dashes. If the page fails to start, it shows the error instead of a blank page. --- # AI assistants (MCP) `shine-mcp` is a [Model Context Protocol](https://modelcontextprotocol.io) server that speaks JSON-RPC over stdio. It ships with the desktop release, or build it with `cargo build --release --bin shine-mcp`. ## Connecting a client ```json { "mcpServers": { "shine": { "command": "/path/to/shine-mcp" } } } ``` In Claude Code: `claude mcp add shine /path/to/shine-mcp`. ## Tools | Tool | What it does | |---|---| | `list_blocks` | Lists the building blocks for circuits: type name, ports, parameters with defaults and units. | | `simulate_circuit` | Solves a circuit exactly at every laser wavelength; returns detector readings, output powers and the field on each connection. | | `sweep_wavelength` | Drives one open input with unit power and returns output powers over a wavelength range: spectra of filters, rings, interferometers. | | `decompose_matrix` | Finds MZI phase settings for a matrix: `unitary` gives a triangular (Reck) mesh, `svd` handles any matrix with two meshes and attenuators. | | `simulate_project` | Opens a `.shine` project and simulates every document: detector readings, display values, signal outputs, component interfaces. | | `save_project` | Writes a circuit as a `.shine` project that a person can open, inspect and edit in the editor. | Circuits are JSON: `blocks` (each with an `id`, a `type` from `list_blocks` and optional `params`) and `connections` from an output `"block.port"` to an input `"block.port"` (port name or 0-based index). Each port takes one connection. A connection without `length_um` is ideal (no phase); with it, the phase uses `n_eff` (default 2.4). ```json { "blocks": [ {"id": "laser", "type": "laser", "params": {"wavelength_nm": 1550}}, {"id": "dc", "type": "coupler"}, {"id": "pd", "type": "detector"} ], "connections": [ {"from": "laser.out", "to": "dc.in1"}, {"from": "dc.out2", "to": "pd.in"} ] } ``` ## Notes for language models - Call `list_blocks` first; parameter names and units come from there, not from memory. - Simulate before claiming a result, and quote the numbers returned. - Powers are normalised: a laser of amplitude 1 delivers power 1. - Wavelengths are in nanometres, lengths in micrometres, phases in radians. - Saved projects open in the editor, where the circuit appears as boxes joined by waveguides; tell the user where you saved the file. - The editor's free-space parts and electronics (registers, function cells) aren't part of the MCP circuit format; `simulate_project` does simulate them in saved projects. - The documentation in plain text: . --- # Rust library The simulator is the `shine::photonics` module and works without the editor. Disable default features to leave out the GUI: ```toml [dependencies] shine = { git = "https://github.com/ash-hashtag/shine", default-features = false } ``` ## A Mach–Zehnder interferometer ```rust use shine::photonics::*; let mut c = Circuit::new(); let laser = c.add(Laser::new(1.0, 1550.0)); let split = c.add(DirectionalCoupler::balanced()); let heat = c.add(PhaseShifter::new(std::f64::consts::PI)); let join = c.add(DirectionalCoupler::balanced()); let pd = c.add(Detector::new()); c.connect(laser.output(0), split.input(0))?; c.connect(split.output(0), heat.input(0))?; c.connect(heat.output(0), join.input(0))?; c.connect(split.output(1), join.input(1))?; c.connect(join.output(0), pd.input(0))?; let power = c.simulate(1550.0, &[])?.detected_power(pd); // 1.0 ``` Conventions: fields are complex amplitudes with |E|² = power; wavelengths in nm, lengths in µm; a lossless coupler is `[[t, ik], [ik, t]]`. ## Compile once, run many times `Circuit::compile(wavelength_nm)` turns a circuit into a `Program`: straight paths become SIMD multiply-adds and feedback loops get an LU factorisation computed once. Run it with one set of inputs (`run`) or a whole batch (`run_batch`, one column per input vector). `Circuit::replace` swaps a part's parameters (for example a heater's phase) and keeps the wiring. ## Blocks `photonics::blocks` has reusable sub-circuits you add with `Circuit::add_block`: splitter and combiner trees, all-pass and add–drop rings (`AddDropRing::resonant` picks the length for a wavelength), interleavers, MZI switch trees, the optical DFT, a WDM demultiplexer, and MZI meshes. ## Programming MZI meshes `UnitaryMesh::decompose(&u)` finds MZI phases for a unitary matrix (a triangular, Reck mesh). `SvdMesh::decompose(&m)` handles any matrix: two meshes and a row of attenuators, `M = scale · U · diag(σ) · V†`. See `examples/matmul.rs`: ```sh cargo run --release --no-default-features --example matmul ``` The full API is documented in the source (`cargo doc --no-default-features --open`). --- # Development ## Build and run ```sh cargo run --release # the editor cargo run --release --bin shine-mcp # the MCP server cargo test # every test, including headless UI tests cargo clippy --all-targets # lints (CI fails on warnings) ``` On Linux the editor needs the usual windowing libraries (`libxkbcommon`, `libxcb`, Wayland or X11 at runtime). ## The website and browser build `web/` is an Astro site: the home page, the section pages (`/features`, `/library`, `/ai`), these docs at `/docs` (rendered by [Starlight](https://starlight.astro.build) straight from `docs/*.md`, with Pagefind search built in), `/llms.txt`, and `/app` (the editor compiled to WebAssembly). It uses pnpm. Search works in built sites, not under `pnpm dev`. ```sh scripts/build-web.sh # WebAssembly + site into web/dist scripts/build-web.sh --screenshots # also regenerate every screenshot cd web && pnpm dev # live preview cd web && pnpm run deploy # publish to Cloudflare (maintainers) ``` The browser build needs the `wasm32-unknown-unknown` target and a `wasm-bindgen` CLI matching the version in `Cargo.lock`; the script checks. ## Keeping the docs current The documentation lives in `docs/*.md` (readable on GitHub, rendered on the website and in `/llms-full.txt`). When you change behaviour, update the page that describes it in the same change. A test (`cargo test docs`) fails when the docs fall behind the code: every part in the library, every example, every keyboard shortcut and every MCP tool must be mentioned. Screenshots in `docs/img/` and `web/public/screenshots/` come from the real app: `scripts/build-web.sh --screenshots` regenerates them with headless Chromium, including the interactive ones (menus, the parts browser) driven by `scripts/ui-screenshots.mjs`. The same run renders a 1200×630 social card for the home page and each docs page (`scripts/og-cards.py`, into `web/public/og/`), so new pages get one too. ## Search engines and link previews Every page has a canonical link, Open Graph and Twitter card tags, and schema.org data: the home page describes the app (`SoftwareApplication`, `WebSite`), and each docs page is a `TechArticle` with breadcrumbs and its last-modified date from git. `sitemap.xml` lists the home page and every docs page with their dates and images; `/app` and the 404 page are `noindex`. Add new indexable pages to `web/src/pages/sitemap.xml.ts`, and their social card to `scripts/og-cards.py`. ## Continuous integration and releases - **CI** (`.github/workflows/ci.yml`) runs on every push and pull request: tests, clippy, the WebAssembly check, and the website build. - **Releases** (`.github/workflows/release.yml`) run only when a version tag is pushed. They build `shine` and `shine-mcp` for Linux (x86-64, ARM64), Windows (x86-64) and macOS (Apple silicon, Intel), and publish them with checksums on a GitHub release. The archives keep the same names in every release (`shine-linux-x86_64.tar.gz`, `shine-windows-x86_64.zip`…), so the website's download buttons always fetch the latest. To release, once a version is final: ```sh # 1. set `version` in Cargo.toml (e.g. 0.2.0) and give it a section in # docs/changelog.md ("## 0.2.0 (2027-01-15)"); move done items off the roadmap # 2. commit, push, then tag that commit and push the tag git tag v0.2.0 git push origin v0.2.0 ``` The workflow stops if the tag doesn't match the version in `Cargo.toml` or the changelog has no section for it, and uses that section as the release notes. Keep `docs/changelog.md` up to date as changes land (under the next version's heading), and tick items off in `docs/roadmap.md`. --- # Roadmap Shine today is a sound simulator for learning, teaching and early design: exact interference and resonance, reusable components, and the electronics around them. The roadmap is about making it useful for engineers designing real photonic systems, in this order: answer the questions engineers ask, get the numbers right, get the data out, then connect to real foundry models. What's already shipped is in the [Changelog](https://shine.lupyd.com/docs/changelog/). Ideas and requests are welcome as [GitHub issues](https://github.com/ash-hashtag/shine/issues). ## 0.1.0: released 2026-11-09 - [x] One editor for free-space optics, component boxes, waveguides and signals - [x] Exact frequency-domain solver: interference, resonance, feedback loops - [x] Reusable, nested components; registers, a clock and function cells - [x] Coherent and independent light sources; real and false colours - [x] Desktop (Linux, Windows, macOS) and browser builds; MCP server; documentation ## 0.2: useful to engineers (next) - [ ] **Wavelength sweeps in the editor**: transmission versus wavelength, in dB, for any design, from a chosen source or input to chosen outputs - [ ] **Parameter sweeps**: sweep a part's parameter and plot the outputs - [ ] **Real units**: laser power in mW or dBm, readings in mW and dBm, losses in dB, insertion loss and extinction ratio readouts - [ ] **Waveguide physics**: group index and dispersion, propagation loss in dB/cm, wavelength-dependent coupling, so ring spacing and delays match real devices - [ ] **Export**: sweep and readout data as CSV, plots and the canvas as images - [ ] **Validation page**: simulated results plotted against textbook formulas (rings, MZI, Fabry–Pérot, couplers, the DFT mesh) - [ ] **Release basics**: an About box with the version, release notes, and instructions for unsigned binaries ## 0.3: connect to real designs - **S-parameter import** (Touchstone `.sNp`): use foundry kit (PDK) and FDTD results as black-box components - **Command-line simulation**: `shine simulate project.shine --sweep … --csv`, for scripts and CI - **Monte Carlo**: how a design holds up to fabrication variation - **Two polarisations** per port ## Later - Layout export (GDS), for example through gdsfactory - Time-domain simulation: modulation, bandwidth, eye diagrams and noise - Signed and notarised desktop builds - A photon-counting and few-photon (quantum) teaching mode --- # Changelog Notable changes in each version. Releases are tagged on [GitHub](https://github.com/ash-hashtag/shine/releases), where each version's notes come from this page. What's planned is on the [Roadmap](https://shine.lupyd.com/docs/roadmap/). ## 0.1.0 (2026-11-09) The first release. ### Editor - One grid for free-space optics, component boxes, waveguides and electrical signals, with Blender-style controls (G, R, Shift+D, Shift+A, box select, undo and redo). - A library in collapsible folders with search, a Parts & components browser, and an add menu with a submenu per category. - Reusable components: any layout becomes a box with light ports and signal pins; Tab opens its definition; edits update every use. In its own tab, a component's input pins can be clicked to try it alone. - Wiring by dragging between ports. Connecting light to a signal (or the reverse) warns and offers to insert the converter: a detector or a laser. - Preferences, saved between sessions: theme, grid, port names, light colours, channel strands, beam arrows, readings, default laser wavelength, live simulation, clock speed. - Light colours: real colours for visible wavelengths, distinct false colours for infrared and ultraviolet, or a colour per laser; one strand per wavelength on shared waveguides. ### Simulation - An exact frequency-domain solver: each circuit compiles per wavelength into SIMD multiply-adds, with an LU factorisation for each feedback loop. - Laser sources: independent lasers add power; lasers on a shared source (A–H) interfere. All sources at a wavelength are solved together in one vectorised pass. - Electronics: threshold detectors, driven lasers, phase shifters and attenuators, number inputs, displays, clocked registers, and function cells with an expression language. Optics and electronics settle together; oscillating loops are reported. - Free-space parts, waveguide blocks (couplers, MZIs, rings, add–drop rings, splitter and switch trees, the optical DFT) and nested components in one circuit. ### Examples - A 4-channel WDM optical link, an 8-bit adder and an accumulator (as an electro-optic teaching model), free-space interferometers and cavities, and chip circuits with spectra: ring filters, interleaver, WDM demultiplexer, switch tree, Fourier transform, matrix multiplier. ### Files, platforms and integrations - Projects in a compact binary `.shine` format (format 3); older files are upgraded on open. - Desktop apps for Linux (x86-64, ARM64), Windows and macOS (Apple silicon, Intel), and the same editor in the browser. - `shine-mcp`, a Model Context Protocol server for AI assistants. - The `shine::photonics` Rust library. - Documentation with screenshots, search, and plain-text versions for language models.