# Fiber Optic

Aperveil / Light, shaped for the web.

A WebGPU bundle of side-glow fibres behind a working focal plane: light fades along each strand as it is lost, leaves through a bright cut tip, and runs in pulses from source to end, while depth sets each strand's blur.

[Aperveil integration guide](https://www.aperveil.com/docs)

## What it does

Light moves through each strand the way it does through a side-glow fibre. It enters at the source end and is lost along the way, I = I₀ e^−αs, so a strand is brightest where it leaves the bundle and dims toward its tip; what is left at the end leaves through the cut face, which is why each tip is a point brighter than the strand that feeds it. Every strand has its own length, so the tips land at different places, and pulses of light run from the source to the tip, attenuated on the way like the rest. The strands are arcs around a shared origin whose radius breathes with angle, so the bundle braids rather than stacking rings. Each carries a depth: the circle of confusion opens with the distance from the focal plane and the strand's energy is spread across it, so an out-of-focus strand is wide and dim while a focused one stays a bright hairline — and an out-of-focus tip images as a disc, dimming as the square of its blur.

## Choose it for

Open an infrastructure, network or audio product with a layer that has real depth to it. Keep the copy over the dark side of the sweep and let the visitor rack focus across the bundle. The strands stay behind ordinary HTML.

## Limits

A lit-strand renderer, not a waveguide: there are no modes, no numerical aperture, no total internal reflection and no dispersion. The loss is a single exponential and the side glow is a fixed fraction of the light carried. The depth of field is one circle-of-confusion term, not a lens model with a real focal length, aberration or a shaped iris. The pointer moves the focal plane and nothing else.

## Current look

Default. Color Looks change colors only; motion, geometry and typography remain unchanged.

[Open editor](https://www.aperveil.com/shaders/fiber-optic/edit?look=default&scene=landing)

## Landing scene

This preview uses `origin`: [1.08,1.55], `spread`: 1.6, `arcOffset`: -0.64, `arcSweep`: -0.3, `twist`: 0.35, `braid`: 0.12, `strands`: 40, `glow`: 2.6. The editor link carries these settings with your Look. Direct editor visits use the product defaults; Reset all restores those defaults.

## Install

Download the Export Kit from the editor. No shader-specific npm package or CLI is published yet.

```sh
npm install ./vendor/aperveil-runtime-0.1.0.tgz ./vendor/aperveil-react-0.1.0.tgz
```

Every control in the editor is a typed prop. Leave a prop out to keep the value you exported; change one and the running shader updates without restarting. Give the surface a size and pass fallback for browsers without WebGPU.

```tsx
"use client";

import FiberOptic from "./FiberOptic";

export default function Hero() {
  return (
    <FiberOptic
      className="hero-shader"
      spread={1.3} colorNear="#3aa0ff"
      fallback={<div className="hero-shader-fallback" />}
    />
  );
}

// .hero-shader { width: 100%; height: 560px; }
```

Component props: `origin`, `spread`, `arcSweep`, `arcOffset`, `strands`, `braid`, `twist`, `taper`, `sparkle`, `attenuation`, `pulses`, `coreWidth`, `aperture`, `focus`, `depthFade`, `glow`, `flowSpeed`, `focusSweep`, `interaction`, `colorNear`, `colorFar`, `colorSpark`, `background`, `opacity`, `backgroundOpacity`, `className`, `style`, `paused`, `fallback`, `onReady`, `onError`.

Raw WGSL is included for a custom WebGPU host; it is not CSS. Keep semantic headings and links in HTML.

## Controls

- Bundle origin (`origin`, xy): Point the strands fan out from; it may sit outside the frame.
- Bundle spread (`spread`, slider): How far apart the strands fan out from one another.
- Arc sweep (`arcSweep`, slider): How much of the circle each strand runs through. The sign picks the end the light enters from, so a negative sweep fans the bundle out the other way.
- Arc rotation (`arcOffset`, slider): Turns the whole fan of strands around its origin.
- Strand count (`strands`, slider): Number of fibres in the bundle.
- Braid (`braid`, slider): How far each strand's radius breathes with angle, which is what lets strands cross.
- Fan twist (`twist`, slider): How much the strands' directions diverge across the bundle.
- Source taper (`taper`, slider): How softly a strand leaves the bundle at its source end. The tip is always a cut.
- Tip glow (`sparkle`, slider): Brightness of the light leaving each strand's cut end. It is what the fibre still carries at the tip, so it dims with attenuation; out of focus it images as a disc.
- Attenuation (`attenuation`, slider): Light lost per strand length, I = I0 exp(-alpha s). Higher values dim the strands toward their tips and leave less to exit the cut end.
- Signal pulses (`pulses`, slider): Brightness of the pulses running from the source to each tip, attenuated on the way like the rest of the light.
- Core width (`coreWidth`, slider): Width of a strand in focus, measured in device pixels so it stays equally fine at any DPR.
- Aperture (`aperture`, slider): How fast the circle of confusion opens away from the focal plane.
- Focal plane (`focus`, slider): Which depth is in focus, from the back of the bundle to the front.
- Depth fade (`depthFade`, slider): How much dimmer the farther strands are.
- Glow (`glow`, slider): Brightness of the strands and their tips.
- Braid speed (`flowSpeed`, slider): Speed of the braiding motion and the travelling light pulses.
- Rack range (`focusSweep`, slider): How far the pointer walks the focal plane across the bundle's depth.
- Pointer focus pull (`interaction`, slider): How far moving the pointer sideways pulls the focus.
- Near strands (`colorNear`, color): Colour of the strands nearest you.
- Far strands (`colorFar`, color): Colour of the farthest strands.
- In-focus core (`colorSpark`, color): Colour of the sharp in-focus cores, the travelling light pulses and the cut tips.
- Background (`background`, color): default "#03050c"
- Opacity (`opacity`, slider): default 1
- Background opacity (`backgroundOpacity`, slider): default 1

## Compatibility

WebGPU rendering requires a supported browser and secure context. The landing shows a real poster for the selected Look when live rendering is unavailable. The standard exported component does not bundle a poster fallback. Supply your own fallback in the host layout. Text exports also retain readable HTML typography.

## Performance

6.619 ms GPU timestamp median; p95 6.685 ms. 1440x900 @ dpr 1.5; Metal driver on macOS Version 26.6.2 (Build 25G83); measured 2026-09-18. This is a recorded headless sample, not a browser FPS or mobile-performance guarantee.

## Questions

### Why are some strands sharp and others just a glow?

Each strand sits at its own depth. The further it is from the focal plane, the wider its circle of confusion, and because its light is spread over that width it also gets dimmer. That pairing is what makes the blur read as depth rather than as softness.

### Why do the strands fade toward their ends?

Attenuation. A fibre loses light along its length, I = I₀ e^−αs, so the side glow is strongest where the strand leaves the bundle. Raise Attenuation for a steeper fade and dimmer tips, or set it to zero for strands that glow evenly and end in their brightest point.

### Why is the end of each strand brighter than the strand?

Because that is where the light leaves. Along the strand only a little escapes through the side, and the rest is lost as it travels; whatever is still carried at the end comes out of the cut face as a point. Out of focus, a point images as a disc, so its light spreads over the area of its blur and dims as the square of it.

### Does it stay crisp on a retina screen?

Yes. Core width is set in device pixels rather than in canvas units, so the sharpest strand is the same fine line at any device pixel ratio instead of thickening with the backing texture.

### What does the cursor do?

It racks focus: moving it left and right walks the focal plane through the bundle's depth, so different strands come forward into sharpness. Nothing in the geometry moves. Set Pointer focus pull to zero for a fixed plane.

### Can I make the bundle denser?

Strand count goes up to 40, and Braid controls how far each strand's radius breathes, which is what lets them cross rather than nest. Both cost GPU time, so the measured timing on this page is for the shipped defaults.

### What happens without WebGPU?

This landing shows a still poster of your selected Color Look. The raw WGSL needs a WebGPU host. Landing posters are not bundled in the Export Kit: add your own fallback around the exported component.

### Can I use this shader commercially?

Part of the Free collection for Aperveil 1.0. Public live preview and Color Looks are open. Free Export Kits do not require an account. Purchasing is not available yet. Follow the license notices supplied with the source you download. The planned Free collection permits personal and commercial interface projects. An alpha access grant is not a purchase or a replacement for those license terms.

## License

Part of the Free collection for Aperveil 1.0. Public live preview and Color Looks are open. Free Export Kits do not require an account. Purchasing is not available yet. Follow the license notices supplied with the source you download. The planned Free collection permits personal and commercial interface projects. An alpha access grant is not a purchase or a replacement for those license terms.

## Related shaders

- [Line Waves](https://www.aperveil.com/shaders/line-waves)
- [Signal Weave](https://www.aperveil.com/shaders/signal-weave)
- [Cursor Comet](https://www.aperveil.com/shaders/cursor-comet)
