# DNA helix

A rotating double helix in glyphs, with lit backbones that pass in front of each other and base-pair rungs coloured by base.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. The asciicn agent skill, for building any ASCII effect on the engine, is at [/.well-known/agent-skills/asciicn/SKILL.md](/.well-known/agent-skills/asciicn/SKILL.md).

> **This is an example, not the only option.** It is one component built on the asciicn engine. Install it when it is what you were asked for. For any other look, don't stretch its props to fit: write your own field on `AsciiShader` ([Write your own](/docs/engine/write-your-own.md)), or draw a CPU grid with `AsciiGrid`, and read this example's source for technique. Its full source is in [https://asciicn.dev/r/dna-helix.json](https://asciicn.dev/r/dna-helix.json), under `files[].content`.

## Installation [#installation]

```bash
npx shadcn@latest add https://asciicn.dev/r/dna-helix.json
```

## Usage [#usage]

```tsx
import { DnaHelix } from "@/components/asciicn/dna-helix";

export function Hero() {
  return <DnaHelix className="h-96 w-64" />;
}
```

The two backbones and every rung are built from lit spheres, drawn into a depth buffer one sample at a time. Where a strand swings behind the other, or a rung passes behind a backbone, the nearer surface wins, so the helix stays readable at any angle and in any charset. Braille samples each cell 2×4 times, which gives the finest strands.

The strands sit less than half a turn apart, as in real DNA, which opens a wide groove and a narrow one. Each rung is one base pair: A with T, or G with C. In truecolor each half takes its own base's colour, with a hairline gap in the middle where the pair bonds. The far side of the helix fades towards the paper, and the whole molecule slides slowly along its axis.

It does not react to hover. Press and drag sideways to spin it. When it stands upright, drag up or down to tilt it. Let go mid-drag and it keeps spinning, then settles back to its own pace. With keyboard focus, the arrow keys spin and tilt it.

## Examples [#examples]

### Beside a headline [#beside-a-headline]

Upright in a narrow column on a transparent background, so it sits on the page instead of in a box.

```tsx title="examples/dna-helix-feature.tsx"
import { DnaHelix } from "@/components/asciicn/dna-helix";

export default function DnaHelixFeature() {
  return (
    <section className="grid w-full max-w-3xl items-center gap-6 sm:grid-cols-[1fr_minmax(0,16rem)]">
      <div className="flex flex-col gap-3">
        <p className="font-mono text-muted-foreground text-xs uppercase tracking-widest">
          Sequencing · run 0417
        </p>
        <h3 className="text-balance font-medium text-3xl tracking-[-0.03em]">
          Read a genome before your coffee cools
        </h3>
        <p className="text-pretty text-muted-foreground text-sm">
          Whole-genome runs in 41 minutes at 30x coverage, with variant calls
          streamed to your pipeline as each lane finishes.
        </p>
      </div>
      <DnaHelix
        cell={8}
        speed={0.3}
        scroll={0.4}
        interactive={false}
        background="transparent"
        className="h-80 w-full sm:h-96"
      />
    </section>
  );
}
```

### Your own sequence [#your-own-sequence]

`sequence` repeats any string of A, C, G and T along one strand, and the other strand pairs with it. Here it is GATTACA in the classic charset, with its own `baseColors`.

```tsx title="examples/dna-helix-terminal.tsx"
import { DnaHelix } from "@/components/asciicn/dna-helix";

export default function DnaHelixTerminal() {
  return (
    <div className="w-full max-w-xl border bg-background font-mono text-xs">
      <div className="flex items-center justify-between border-b px-3 py-1.5 text-muted-foreground">
        <span>~/lab $ helix --seq GATTACA</span>
        <span>7 bp</span>
      </div>
      <DnaHelix
        orientation="horizontal"
        sequence="GATTACA"
        charset="standard"
        baseColors={{ A: "red", C: "purple", G: "orange", T: "blue" }}
        speed={-0.45}
        cell={11}
        className="aspect-[2/1] w-full"
      />
    </div>
  );
}
```

## Props [#props]

Every [ASCII image](/docs/images/ascii-image#props) look prop also applies, with `charset` defaulting to `"braille"`, plus `fx` for a CRT finish.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `speed` | `number` | `0.35` | Spin around the helix axis, in radians per second. Negative spins the other way. |
| `twist` | `number` | `1` | How tightly the strands wind. 1 is the shape of B-DNA, one turn every 3.4 radii; 2 winds twice as tight. |
| `rungs` | `number` | `10` | Base pairs per turn, 4 to 20. |
| `scroll` | `number` | `0.5` | Base pairs per second that slide along the axis. 0 holds the helix in place. |
| `orientation` | `"vertical" \| "horizontal"` | `"vertical"` | Which way the helix axis runs across the box. |
| `sequence` | `string` | `A fixed mixed sequence` | Bases read along one strand and repeated. Letters other than A, C, G and T are skipped. |
| `color` | `AsciiColor` | `"--ascii-ink"` | Backbone colour in truecolor. |
| `baseColors` | `Partial<Record<"A" \| "C" \| "G" \| "T", AsciiColor>>` | `{ A: "green", T: "pink", G: "orange", C: "blue" }` | Rung colours by base in truecolor. |
| `colorMode` | `"mono" \| "truecolor" \| "ansi16" \| "duotone" \| "palette"` | `"truecolor"` | How glyphs are coloured. |
| `interactive` | `boolean` | `true` | Drag or use the arrow keys to spin and tilt it. |
| `cell` | `number` | `10` | Glyph cell height in CSS pixels. |
| `animate` | `boolean` | `true` | false draws one still frame. |
| `handle` | `RefObject<AsciiHandle \| null>` |  | Filled with a handle for export. |

## Notes [#notes]

* It is announced as an image. Pass `aria-label` to describe what it stands for on your page.
* Under reduced motion it draws one still frame and stays put.
* It draws at up to 60 fps on the CPU and pauses offscreen and in hidden tabs. The cost grows with the number of samples, so a smaller box or a larger `cell` is cheaper.
* On a touch screen, vertical swipes scroll the page. Sideways swipes spin the helix.