Captain Claude

auditedFree

Captain Claude sails a two-masted brig with blood-red sails across a truecolor sunset, in two animated lines. Gerstner wave physics: deep-water dispersion, slope-lit swells, breaking whitecaps, a glinting sun lane. Pure scene, displays nothing. refreshInterval 1 + truecolor.

by rsproule1 installspiratesunsetanimatedtwo-linephysics
Claude Code
>
? for shortcuts⏵⏵ accept edits on
Security auditby claude-opus-4-8

A purely cosmetic statusline that renders an animated two-line ASCII/Unicode scene: a pirate ship sailing across a truecolor sunset over a wave field. It reads session JSON from stdin but deliberately ignores it, reads the epoch time (via date) and the COLUMNS/BOAT_T/WATER environment variables for layout and animation, then pipes computed parameters into a single awk program that prints ANSI 24-bit color escape sequences. It performs no network activity, writes no files, and spawns no subprocesses beyond awk and date.

subprocessesenv-vars

Audits are advisory. This script runs on your machine — read it before installing.

The script — read it before you run it

#!/usr/bin/env bash
# Claude Code status line — a sailboat silhouetted against a sunset, in two
# lines. Requires "refreshInterval": 1 (and a terminal with 24-bit color).
#
#   line 1 — sky: indigo-to-amber gradient pooling around a low sun, rosy
#            clouds drifting slowly westward
#   line 2 — sea: rolling swells, whitecaps, the sun's golden reflection
#            lane glinting. Pure scene — deliberately displays nothing.
#
# The ship is a two-masted pirate brig spanning both lines: blood-red
# square sails billowed wide at the top (▜█▛│▜█▛) over a walnut plank hull
# (▜███████▛), bow spray ahead, fading wake astern. She sails edge to edge,
# comes about, and crosses the sun on every pass. Clouds drift opposite for
# parallax. Plain single-width text: no emoji anywhere.
#
# No network, no state, no subprocesses beyond jq/awk. Test with:
#   echo '{}' | COLUMNS=100 ~/.claude/statuslines/boat-over-sea
#   (set BOAT_T to a fixed epoch to render a specific frame)

input=$(cat)   # session JSON on stdin — unused; the scene shows nothing
export LC_ALL=en_US.UTF-8

t=${BOAT_T:-$(date +%s)}
# Water styles (preview with WATER=n in a terminal loop):
#   1 rolling  — long swells all traveling toward the sunset (calmest)
#   2 crossing — two swell systems moving against each other; foam breaks
#                where they collide (default)
#   3 headwind — the sea always streams against the ship's heading, so her
#                speed reads in the water rushing past the hull
#   4 squall   — fast multi-directional chop, extra whitecaps
#   5 ocean    — true wave physics: a Gerstner wave field obeying the
#                deep-water dispersion relation ω=√(gk), so long swells
#                genuinely outrun short chop; short components get
#                trochoidal crest-sharpening, and foam breaks where the
#                surface steepness exceeds the spilling threshold.
#                Drawn as a filled eighth-block height field (▁▂▃▄▅▆▇), and
#                the whole field streams against the ship's heading, so her
#                speed reads in the water running past. (default)
#   6 trace    — same physics, drawn as a dotted waveform: dots at five
#                sub-cell heights (. ∙ · ˙ ˚) trace the slopes, arcs (◡ ◠)
#                cap the flat troughs and crests
WATER=${WATER:-5}
W=$(( ${COLUMNS:-80} - 4 ))   # margin for Claude Code's own padding
[ "$W" -lt 40 ] && W=40

awk -v W="$W" -v t="$t" -v water="$WATER" 'BEGIN{
  # ---- layout ---------------------------------------------------------------
  xs  = W - 10;                      # sun disc: 3 cells at xs..xs+2
  xsc = xs + 1;                      # center of the sun / reflection lane

  span   = W - 9;                    # hull is 9 cells wide
  period = 2 * span;
  x   = t % period;
  fwd = (x < span);
  pos = fwd ? x : period - x;        # left edge of the hull

  # ---- line 1: sky ------------------------------------------------------------
  sky = "";
  for (c = 0; c < W; c++) {
    d = c - xsc;
    m = 1 / (1 + (d/16.0)^2);        # sunset glow falloff
    R = 28  + 222*m;
    G = 30  + 118*m;
    B = 74  -  20*m;

    # two rosy cloud banks drifting west (opposite the sunset glow)
    for (k = 0; k < 2; k++) {
      cw = 7 - 2*k;
      cc = (W + 14) - ((t/3 + k*37) % (W + 28));
      cd = c - cc; if (cd < 0) cd = -cd;
      if (cd < cw) {
        f = (1 - cd/cw) * 0.55 * (1 - 0.5*m);
        R = R*(1-f) + 214*f;
        G = G*(1-f) + 138*f;
        B = B*(1-f) + 152*f;
      }
    }

    ch = " "; fR = R; fG = G; fB = B;

    if (c >= xs && c <= xs+2) {      # the sun, half-sunk on the horizon
      ch = "\xe2\x96\x84";           # ▄
      fR = 255; fG = 224; fB = 138;
    }
    # the rig: two blood-red square sails, billowed wide at the top (▜█▛),
    # foremast and mainmast dark between them
    i = c - pos;
    if (i == 1 || i == 5) { ch = "\xe2\x96\x9c"; fR = 128; fG = 30; fB = 34; }  # ▜
    if (i == 2 || i == 6) { ch = "\xe2\x96\x88"; fR = 158; fG = 38; fB = 42; }  # █
    if (i == 3 || i == 7) { ch = "\xe2\x96\x9b"; fR = 128; fG = 30; fB = 34; }  # ▛
    if (i == 4)           { ch = "\xe2\x94\x82"; fR = 46;  fG = 30; fB = 24; }  # │ mast

    sky = sky sprintf("\033[48;2;%d;%d;%dm\033[38;2;%d;%d;%dm%s",
                      R, G, B, fR, fG, fB, ch);
  }

  # ---- line 2: sea ------------------------------------------------------------
  sea = "";
  for (c = 0; c < W; c++) {
    d = c - xsc;
    lane = 1 / (1 + (d/5.0)^2);      # the sun road on the water
    R = 12 + 150*lane;
    G = 42 +  56*lane;
    B = 88 -  36*lane;

    # swell field, per water style
    foam_at = 0.86; fleck = 2; breaking = 0;   # fleck: random whitecap threshold (2 = off)
    if (water == 1) {            # rolling: everything toward the sunset
      h  = 0.55*sin(0.42*c - 0.85*t)
         + 0.35*sin(0.19*c - 0.33*t + 1.7)
         + 0.18*sin(0.83*c - 1.70*t + 4.2);
    } else if (water == 3) {     # headwind: sea streams against the heading
      wdir = fwd ? 1 : -1;
      h  = 0.50*sin(0.45*c + wdir*1.3*t)
         + 0.32*sin(0.21*c + wdir*0.5*t + 1.5)
         + 0.22*sin(0.85*c + wdir*2.4*t + 3.1);
    } else if (water == 4) {     # squall: fast crosschop, lots of white
      h  = 0.45*sin(0.50*c - 1.6*t)
         + 0.40*sin(0.33*c + 1.9*t + 2.4)
         + 0.30*sin(1.10*c - 3.4*t + 0.7);
      foam_at = 0.78; fleck = 0.90;
    } else if (water == 2) {     # crossing: opposed swells collide and break
      h  = 0.48*sin(0.42*c - 1.10*t)
         + 0.42*sin(0.36*c + 0.85*t + 2.1)
         + 0.20*sin(0.90*c - 2.60*t + 1.2);
      foam_at = 0.88;
    } else {                     # ocean: Gerstner field with real dispersion
      # five components: wavelength L (cols), amplitude a, direction dn.
      # each travels at its own phase speed sqrt(g/k) — long waves outrun
      # short ones, so the surface never moves as one block. g is scaled to
      # keep every component under the 1 fps Nyquist rate.
      g = 2.5;
      nL[1]=36;  na[1]=0.42; nd[1]= 1; np[1]=0.0;   # primary swell
      nL[2]=17;  na[2]=0.27; nd[2]= 1; np[2]=2.1;
      nL[3]=8;   na[3]=0.16; nd[3]= 1; np[3]=4.0;   # wind chop (sharpened)
      nL[4]=4.3; na[4]=0.10; nd[4]= 1; np[4]=1.3;   # ripple    (sharpened)
      nL[5]=23;  na[5]=0.14; nd[5]=-1; np[5]=5.2;   # distant opposing swell
      # the field streams against the heading of the ship and flips when
      # she comes about — her speed reads in the water running past
      wdir = fwd ? -1 : 1;
      h = 0; sl = 0;
      for (j = 1; j <= 5; j++) {
        kk = 6.28319 / nL[j];
        om = sqrt(g * kk);                 # dispersion: omega = sqrt(g k)
        th = kk*c - wdir*nd[j]*om*t + np[j];
        s  = sin(th);
        # trochoidal profile for short components: peaked crests, flat troughs
        if (nL[j] < 10) { u = (s+1)/2; s = 2*u*u - 1; }
        h  += na[j] * s;
        sl += na[j] * kk * cos(th);        # analytic surface slope
      }
      # spilling breaker: forward face too steep near a crest
      slraw = sl;
      if (sl < 0 ) sl = -sl;
      breaking = (h > 0.15 && sl > 0.30);
      foam_at = 0.92;
    }

    k = (h+1)/2; if (k < 0) k = 0; if (k > 1) k = 1;
    R += 16*k; G += 18*k; B += 14*k;   # crests catch light: bg swells too

    if (water == 6) {
      # dotted waveform: the surface as line art. Dots sit at five sub-cell
      # heights tracing the slopes; arcs cap flat crests and troughs.
      if (slraw > 0.10 || slraw < -0.10) {
        lvl = int(k * 4.999);
        if      (lvl == 0) ch = ".";
        else if (lvl == 1) ch = "\xe2\x88\x99";        # ∙
        else if (lvl == 2) ch = "\xc2\xb7";            # ·
        else if (lvl == 3) ch = "\xcb\x99";            # ˙
        else               ch = "\xcb\x9a";            # ˚
      } else {
        if      (k > 0.60) ch = "\xe2\x97\xa0";        # ◠ crest
        else if (k < 0.40) ch = "\xe2\x97\xa1";        # ◡ trough
        else               ch = "\xc2\xb7";            # ·
      }
      # thin glyphs need brighter ink to read against the gradient
      fR = 96 + 110*k + 110*lane;
      fG = 150 + 90*k + 30*lane;
      fB = 195 + 55*k - 55*lane;
    } else {
      # filled height field: each column is an eighth-block ▁▂▃▄▅▆▇ sized to
      # the wave height — the surface as a smooth rolling silhouette.
      lvl = int(k * 6.999);
      ch = sprintf("\xe2\x96%c", 129 + lvl);           # U+2581..2587
      fR = 28 + 78*k + 140*lane;
      fG = 88 + 112*k + 40*lane;
      fB = 150 + 78*k - 60*lane;
    }

    # directional sunset lighting from the analytic slope: faces tilted
    # toward the sun catch amber light, back faces fall into teal shadow.
    if (slraw < -0.05) {
      lw = -slraw * 3; if (lw > 1) lw = 1;
      lw *= 0.30 + 0.70*lane;
      fR = fR*(1-lw) + 255*lw; fG = fG*(1-lw) + 186*lw; fB = fB*(1-lw) + 96*lw;
    } else if (slraw > 0.05) {
      sw = slraw * 2.4; if (sw > 1) sw = 1; sw *= 0.45;
      fR = fR*(1-sw) + 18*sw; fG = fG*(1-sw) + 74*sw; fB = fB*(1-sw) + 120*sw;
    }

    # whitewater: breaking faces and wind-torn crests froth up as ▒
    if (h > foam_at || breaking) { ch = "\xe2\x96\x92"; fR = 240; fG = 248; fB = 255; }
    # squall mode: stray wind-torn whitecaps off the crests
    wc = sin(c*269.5 + t*183.3); wc = wc - int(wc); if (wc < 0) wc += 1;
    if (wc > fleck && h > 0) { ch = "\xe2\x88\x98"; fR = 226; fG = 240; fB = 250; }  # ∘

    # golden sparkle inside the reflection lane. In block modes the glint is
    # pure color on the wave itself — never a glyph swap, which would punch
    # dot-shaped holes in the surface. Trace mode may brighten its dots.
    sp = sin(c*127.1 + t*311.7); sp = sp - int(sp); if (sp < 0) sp += 1;
    if (lane > 0.35 && sp > 0.72) {
      if (water == 6) { ch = "\xcb\x9a"; }             # ˚ glint in line art
      fR = 255; fG = 214; fB = 132;
    }

    # wake and bow spray as whitewater: the wave keeps its shape, the ink
    # turns to foam — churned bright at the stern, fading out behind
    wd = fwd ? pos - c : c - (pos + 8);
    if (wd == 1) { fR = 238; fG = 248; fB = 255; }
    if (wd == 2) { fR = 185; fG = 214; fB = 236; }
    if (wd == 3) { fR = (fR+200)/2; fG = (fG+222)/2; fB = (fB+240)/2; }
    bd = fwd ? c - (pos + 8) : pos - c;
    if (bd == 1) {                       # the bow throws a splash upward
      if (water == 6) { ch = "\xcb\x9a"; }                     # ˚
      else { l2 = lvl + 2; if (l2 > 6) l2 = 6; ch = sprintf("\xe2\x96%c", 129 + l2); }
      fR = 245; fG = 250; fB = 255;
    }

    if (c >= pos && c <= pos+8) {    # walnut plank hull ▜███████▛
      i = c - pos;
      ch = (i == 0) ? "\xe2\x96\x9c" : (i == 8) ? "\xe2\x96\x9b" : "\xe2\x96\x88";
      if (i == 0 || i == 8)     { fR = 56; fG = 34; fB = 24; }   # tapered ends
      else if (i % 2 == 1)      { fR = 96; fG = 60; fB = 40; }   # planking
      else                      { fR = 82; fG = 50; fB = 34; }
    }

    sea = sea sprintf("\033[48;2;%d;%d;%dm\033[38;2;%d;%d;%dm%s",
                      R, G, B, fR, fG, fB, ch);
  }

  printf "%s\033[0m\n%s\033[0m", sky, sea;
}'

Reviews

no reviews yet