helixdb

[docs] [github903]

the stepped rungs of a helix between two posts, glinting now and then

companies 64×32 30 fps in colour or one ink 6.8 kB [report a bug]

Yours? The readme snippet puts it in your README, moving, in GitHub's light and dark themes. A post with the video or GIF from the downloads moves too; a link alone shows its card, which holds still.

html

<script type="module" src="https://ascii.rest/ascii.js"></script>

<ascii-art piece="helixdb" options='{"shine":5}'></ascii-art>

No install and no build step. ascii.js defines the <ascii-art> tag, loads the piece from ascii.rest, plays it while it is on screen, and holds the first frame for anyone who prefers reduced motion. Add mono, <ascii-art piece="helixdb" mono>, for one ink.

react

// npm install ascii.rest
import { Ascii } from "ascii.rest/react";
import { helixdb } from "ascii.rest/pieces";

<Ascii piece={helixdb} options={{"shine":5}} />

A client component, so it works in the Next.js app router as it is. Pass the name instead, piece="helixdb", to fetch the piece only when it mounts. Add mono for one ink.

astro

---
// npm install ascii.rest
import Ascii from "ascii.rest/astro";
---

<Ascii piece="helixdb" options={{"shine":5}} />

Renders the first frame on the server, so the page is whole before any script runs, then plays the piece once it loads. Add mono for one ink.

typescript

// npm install ascii.rest
import { mount } from "ascii.rest";
import { helixdb } from "ascii.rest/pieces";

const el = document.querySelector<HTMLCanvasElement>("canvas")!;
const stop = mount(el, helixdb, {"shine":5});

mount plays a piece in a <pre>, or on a <canvas> for the coloured ones, and returns a function that stops it. Every piece, option and frame is typed. Mount it in a <pre> for one ink.

terminal

npx ascii.rest helixdb

Plays it in your terminal, in colour, until you press a key. Add --mono for the terminal's own colour. Add --light for a light terminal. Add --seconds 5 to stop it on its own; npx ascii.rest list shows every piece. In a CLI of your own, play("helixdb", { seconds: 2 }) from ascii.rest/terminal shows it as a splash screen.

agent

npx skills add bas3line/ascii

# then ask your agent:
# add the helixdb piece to my home page

Gives Claude Code, Cursor, Codex or another coding agent the ascii.rest skill: every piece, the snippet for your framework, and these docs to read offline. Then ask in your own words, and it adds helixdb the way your project needs. The skill is on skills.sh.

readme

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://ascii.rest/svg/helixdb.dark.svg">
  <img alt="helixdb" src="https://ascii.rest/svg/helixdb.svg" width="320">
</picture>
helixdb, as a light README shows it
light, helixdb.svg
helixdb, as a dark README shows it
dark, helixdb.dark.svg

An animated SVG that needs no script, so it plays in a GitHub README, which runs none. GitHub shows the dark one in its dark theme. It holds still for anyone who prefers reduced motion.

helixdb.ts

[js] [github]
/*
 * helixdb: the HelixDB logo, two posts joined by a crossbar, stepped rungs
 * stacked above and below it like a helix seen side on, with a glint crossing
 * it every few seconds.
 *
 * Drawn from HelixDB's own mark, from the navbar of helix-db.com: each cell
 * holds the character whose shape best matches the logo's edge through it, and
 * 8 where the logo is solid. On a canvas it is in its cream on a dark page, as
 * on the site, and in its favicon's near-black on a light one; in a <pre> it is
 * one ink, from the same drawing. The logo is a trademark of its owner, shown
 * here to name the company.
 */
import type { Frame, Meta } from "../types.ts";

export interface HelixdbOptions {
  [key: string]: unknown;
  /** Seconds between glints; 0 keeps the logo still. */
  shine: number;
}

export const meta = {
  name: "helixdb",
  category: "companies",
  note: "the stepped rungs of a helix between two posts, glinting now and then",
  cols: 64,
  rows: 32,
  fps: 30,
  options: { shine: 5 },
  // The logo's colour for a light page, then for a dark one; each as drawn, then twice lighter for the glint.
  palette: [
    "#1e1715", "#6d6867", "#bcb9b9", "#ebd9af", "#f2e6cb", "#f9f4e7",
  ],
} satisfies Meta<HelixdbOptions>;

// In a <pre>, in the page's own colour.
const MONO = String.raw`

  888888888888888888888888                          |888888888
  888888888PPPPPP"                                  |888888888
  888888888|                                        |888888888
  888888888888888888888888888888'                   |888888888
  888888888PPPPPPPPPPPP\                            |888888888
  888888888|                                        |888888888
  8888888888qqqqqqqqqqqqqqqqqqqqqqqqqqqqp           |888888888
  888888888888888888888888888888|                   |888888888
  888888888|                                        |888888888
  888888888qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq/  |88888888P
    "888888888888888888888888888888888888""""""""'  |88888P"
       "888|                                        |88P"
          "\________________________________________/"
           d8888888888888888888888888888888888888888b
           d8888888888888888888888888888888888888888b
           d8888888888888888888888888888888888888888b
           d8888888888888888888888888888888888888888b
          _/""""""""""""""""""""""""""""""""""""""""\_
       _p88|                                        |88q_
    _p88888|  .________q8888888888888888888888888888888888q_
  p88888888|  'PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP888888888q
  888888888|                                        |888888888
  888888888|                   .qqqqqqqqqqqqqqqqqqqqp888888888
  888888888|           888888888888888888888888888888888888888
  888888888|                                        |888888888
  888888888|                            .qqqqqqqqqqqp888888888
  888888888|                    qqqqqqqq8888888888888888888888
  888888888|                                        |888888888
  888888888|                                  .qqqqqp888888888
  888888888|                          qqqqqqqqq888888888888888

`;

// On a canvas, and the colour of each cell: an index into the logo's colours.
const ART = MONO;
const INK = String.raw`

  000000000000000000000000                          0000000000
  0000000000000000                                  0000000000
  0000000000                                        0000000000
  0000000000000000000000000000000                   0000000000
  0000000000000000000000                            0000000000
  0000000000                                        0000000000
  000000000000000000000000000000000000000           0000000000
  0000000000000000000000000000000                   0000000000
  0000000000                                        0000000000
  000000000000000000000000000000000000000000000000  0000000000
    0000000000000000000000000000000000000000000000  00000000
       00000                                        00000
          00000000000000000000000000000000000000000000
           000000000000000000000000000000000000000000
           000000000000000000000000000000000000000000
           000000000000000000000000000000000000000000
           000000000000000000000000000000000000000000
          00000000000000000000000000000000000000000000
       00000                                        00000
    00000000  0000000000000000000000000000000000000000000000
  0000000000  000000000000000000000000000000000000000000000000
  0000000000                                        0000000000
  0000000000                   0000000000000000000000000000000
  0000000000           000000000000000000000000000000000000000
  0000000000                                        0000000000
  0000000000                            0000000000000000000000
  0000000000                    000000000000000000000000000000
  0000000000                                        0000000000
  0000000000                                  0000000000000000
  0000000000                          000000000000000000000000

`;

const START = 0.5; // seconds before the first glint
const PASS = 2; // seconds a glint takes to cross
const HALF = 5; // half its width, in cells
const LEAN = 0.9; // cells it shifts left a row down, so it leans like a slash
const SOLID = "8dbqpPYOo0"; // what the glint turns to slashes; thin edges keep their shape

const lines = (art: string) => art.slice(1, -1).split("\n").map((line) => line.padEnd(meta.cols));

export default function helixdb({ shine = meta.options.shine }: Partial<HelixdbOptions> = {}): Frame {
  const { cols, rows } = meta;
  const mono = lines(MONO), art = lines(ART), ink = lines(INK);
  const n = meta.palette.length / 6;
  const every = shine > 0 ? Math.max(shine, PASS + 0.5) : 0;
  // The glint crosses the logo's ink, edge to edge, rather than the whole frame.
  let lo = Infinity, hi = -Infinity;
  for (const pic of [mono, art])
    pic.forEach((line, y) => {
      for (let x = 0; x < cols; x++) if (line[x] !== " ") (lo = Math.min(lo, x + LEAN * y)), (hi = Math.max(hi, x + LEAN * y));
    });
  const span = hi - lo + 2 * HALF;

  return (t, { paper = false, color } = {}) => {
    const pic = color ? art : mono;
    const since = t - START;
    const at = every && since >= 0 ? lo - HALF + (span * (since % every)) / PASS : -Infinity;
    const out: string[] = [];
    for (let y = 0; y < rows; y++) {
      let line = "";
      for (let x = 0; x < cols; x++) {
        let ch = pic[y][x];
        if (ch !== " ") {
          const d = Math.abs(x + LEAN * y - at);
          let k = d < HALF ? 1 - d / HALF : 0;
          k = k * k * (3 - 2 * k);
          if (k > 0.55 && SOLID.includes(ch)) ch = "/";
          if (color) color[y * cols + x] = (paper ? 0 : 3 * n) + (k > 0.6 ? 2 : k > 0.25 ? 1 : 0) * n + parseInt(ink[y][x], 36);
        }
        line += ch;
      }
      out.push(line);
    }
    return out.join("\n");
  };
}