Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

zil

An expression calculator and scripting language with units, dates, exact fractions and big ints.

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

a taste

# 4th Thursday of November
date(2026, 11, 1).nth_weekday(4, "thu")
# → 2026-11-26
# cities are time zones
date("2026-12-25 18:30") to "Sao Paulo"
# → 2026-12-25 21:30:00 -03:00
# units combine and convert
100 km / 2 h to mph
# → 31.0686 mph
# cups to grams
1 cup * density("flour") to g
# → 125.392 g
# money
$25/h to USD/workyr
# → $52,000.00/workyr
# error bars
5 ± 0.1 m * 2
# → 5 ± 0.2 m
# exact decimals
0.1 + 0.2 == 0.3
# → true
# factoring big ints
factors(2 ** 32 + 1)
# → [641, 6700417]
# calculus
integrate(sin, 0, pi)
# → 2
# geography
great_circle("Paris", "Tokyo")
# → 9712.52 km
# fuzzy matching
"recieve".closest(["deceive", "receive", "recipe"])
# → "receive"
# subnets
in_cidr("10.0.3.7", "10.0.0.0/22")
# → true
# odds in plain words
odds(2 ** -64)
# → "1 in 1.84 × 10^19: about as likely as a perfect March Madness bracket (1 in 9.20 × 10^18)"
# charts
(0..20).map(|x| sin(x / 3)).sparkline
# → "▅▆▇▇███▇▆▅▄▃▂▁▁▁▂▂▄▅"
# syntax: x.f(y) is f(x, y)   |x| x * 2 is a lambda   xs |> sum pipes   # comments

Modules

moduleabout
corevalues, printing, type conversions, parsing, help
fsfiles, directories, paths, JSON and CSV
sysscript arguments, environment variables, shell commands, HTTP GET
textcase, splitting, search, regex; encodings, ciphers and more below
text.layoutpad, center, wrap, truncate, dedent; snake_case, camelCase, slugs; words
text.compareedit distance, similarity, the closest of a list, line-by-line diffs
text.translationtranslate text between languages online; cached translations work offline
text.encodingbase64, base32, base58, URL and hex encodings, UTF-8 bytes, hex dumps
text.hashSHA-1/256/512, MD5, HMAC and CRC-32 of strings
text.ciphersCaesar, ROT13, Atbash, Vigenère, Morse, Braille, NATO spelling
text.fancymock case, l33t, uwu, Pig Latin, zalgo, upside-down, 𝐛𝐨𝐥𝐝/𝒮𝒸𝓇𝒾𝓅𝓉/wide
text.placeholderlorem ipsum placeholder text, random team and person names
datasorting and searching any collection; lists, maps, tables, charts below
data.listsranges, map/filter/reduce, building lists
data.mapskeys, values, lookups, merging and transforming maps
data.setsunique values in insertion order: union, intersection, difference
data.tablesrows under named columns: filter, pick columns, sort
data.chartssparklines, bar charts, histograms, plots, heatmaps, progress bars and trees
mathrounding, roots, logs, interpolation, constants; exact fractions and big ints
math.statssums, averages, spread and extremes of lists; units welcome
math.trigsine, cosine and friends; angles as units
math.numtheorygcd, primes, factoring, factorials, modular powers; exact at any size
math.bitscount, test, set, rotate and swap bits
math.baseshex, binary, octal and any base 2-36; digits of a number
math.formattingprintf and format specs, commas, numbers in words, ordinals, Roman numerals
math.randomrandom numbers and picks, dice, odds in plain words, fortunes, a magic 8-ball
math.uncertaintymeasurements with an error, 5 ± 0.1 m, carried through arithmetic
math.complexcomplex numbers, 2 + 3i; a number touching i is imaginary
math.linalgdot and cross products, matrix multiply, determinant, inverse, linear systems
math.calculusnumeric roots, derivatives and integrals of functions
unitsnumbers with units, combined and converted; live currency rates
units.constantsphysical constants (CODATA 2018); Planck’s constant is h_planck
units.moneycurrencies, live exchange rates, paid time like workday and workyr
units.goofybananas for scale, smoots, fortnights; every item also works as item_for_scale
units.kitchencups to grams with ingredient densities
financepay, interest and growth, loans, splitting bills, margins
timedates, durations, date math, relative text
time.calendarsperiods, weekdays, business days, week numbers, month grids, cron, zodiacs
time.zonesthe same moment in UTC, local time, any IANA zone or a city; world clocks
time.skysunrise, sunset, day length and moon phase for a place and date
geocountries and cities: lookups, distance, bearing, nearest city
devJWTs, URLs, semver, file permissions; networks, IDs, colors and codes below
dev.netIP addresses, CIDR blocks, subnets, well-known ports
dev.idsUUIDs, ULIDs and nano IDs; secure passwords and passphrases
dev.colorscolors as “#rrggbb”: RGB/HSL, mixing, lightening, WCAG contrast
dev.codescheck digits for cards, ISBNs and IBANs; HTTP status and MIME type lookups
artclip art, gradients, QR codes, images, animation; frames, fractals, games below
art.framesboxes, cowsay; art side by side, stacked, overlaid, mirrored, rotated, scaled
art.generativeL-systems, fractals, the Mandelbrot set, cellular automata, mazes, starfields
art.gameschess boards from FEN, playing cards, dice faces
art.figletbig-letter banners in FIGlet fonts like slant and shadow, or any .flf file

Advanced examples

Longer recipes that combine several modules.

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

travel and sky

a flight from Seattle to Tokyo: time in the air and local arrival

geo · units · time.zones

leave = date("2026-12-20T13:00-08:00") to "Seattle"
flight = great_circle("Seattle", "Tokyo") / 900 km/h
{in_the_air: flight.parts, landing: leave + flight to "Tokyo"}
# → {in_the_air: "8 h 33 min 5 s", landing: 2026-12-21 14:33:05 +09:00}

UTC hours when it’s 9 to 5 in New York, London and Berlin at once

time.zones · data.lists

office = |t| 9 <= t.hour < 17
day = date("2026-10-12T00:00Z") to UTC
hours = (0..24).map(|h| day + h * 1 h)
hours.filter(|t| office(t to "New York") && office(t to "London") && office(t to "Berlin")).map(|t| t.format("%H:%M"))
# → ["13:00", "14:00"]

hours of daylight on the 21st of each month, equator to arctic

time.sky · geo · data.charts · art.frames

places = ["Singapore", "Cairo", "Paris", "Oslo", "Reykjavik"]
rows = places.map(|p| (1..=12).map(|m| day_length(p, date(2026, m, 21)) / 1 h))
places.map(|p| p.pad(10)).join("\n").beside(rows.heatmap)
# → Singapore  ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
# → Cairo      ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░
# → Paris      ░░▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒░░░░
# → Oslo       ░░░░▒▒▓▓▓▓▓▓▓▓▓▓▒▒░░░░
# → Reykjavik    ░░▒▒▓▓▓▓████▓▓▒▒░░

where the sun sets within the next hour

geo · time.sky · time

cities().filter(|c| now <= sunset(c.name, today) < now + 1 h).map(|c| c.name).take(6)
# → ["Karachi", "Kabul", "Mashhad", "Sharjah", "Quetta", "Yekaterinburg"]

the fastest a ping from New York could ever be: there and back through fiber at 2/3 c

geo · units.constants

["London", "Tokyo", "Sydney"].map(|p| 2 * great_circle("New York", p) / (2/3 * c) to ms)
# → [55.7408 ms, 108.562 ms, 160 ms]

dig straight down from Madrid: where do you come out?

geo

m = city("Madrid")
out = nearest(-m.lat, m.lon - 180)
{city: out.name, country: out.country, apart: great_circle("Madrid", out.name) to km}
# → {city: "Wellington", country: "New Zealand", apart: 19854.7 km}

the further north, the shorter December days: latitude vs. daylight across every city

geo · time.sky · math.stats

cs = cities()
corr(cs.map(|c| c.lat), cs.map(|c| day_length(c.name, date(2026, 12, 21)) / 1 h))
# → -0.96734

the day Oslo gains daylight fastest, and how fast

time.sky · math.stats · time

start = date(2026, 1, 1)
gains = (0..365).map(|i| day_length("Oslo", start + i * 1 d)).deltas
{day: start + (gains.find(gains.max) + 1) * 1 d, gained: gains.max to min, fastest_loss: gains.min to min}
# → {day: 2026-03-16, gained: 5.46001 min, fastest_loss: -5.37941 min}

money and science

a $400k mortgage from January 2027: cost, crossover, interest by year

finance · time · data.lists · data.charts

plan = amortize($400000, 6.5%/yr, 30 yr)
flip = plan.filter(|m| m.principal > m.interest).first
yearly = plan.chunks(12).map(|y| y.map(|m| m.interest).sum / $1)
{monthly: plan[0].payment, interest: plan.map(|m| m.interest).sum, crossover: date(2027, 1, 1) + (flip.n - 1) * 1 mo, by_year: yearly.sparkline}
# → {monthly: $2,528.27, interest: $510,177.95, crossover: 2046-05-01, by_year: "██████▇▇▇▇▇▇▇▆▆▆▆▅▅▅▅▄▄▄▃▃▂▂▂▁"}

a ski trip: who owes whom

finance · data.maps

paid = {ana: $640 + $85.50, ben: 3 * $42, cy: $0, dee: $310}
{each: share(paid.values.sum, 4)[0], settle: settle(paid)}
# → {each: $290.38, settle: ["cy pays ana $290.38", "ben pays ana $144.75", "ben pays dee $19.62"]}

weigh the Earth with a pendulum; errors carry through every step

math.uncertainty · units.constants

L = 1.000 ± 0.005 m
T = (20.07 ± 0.1 s) / 10    # timed 10 swings
g = 4 * pi ** 2 * L / T ** 2 to m/s^2
M = g * (6371 km) ** 2 / G to kg
{g: g, off_by: percent(g.value / g0 - 1, 2), earth: M}
# → {g: 9.80088 ± 0.109272 m/s^2, off_by: "-0.06%", earth: 5.96039e24 ± 6.64533e22 kg}

a ball thrown at 20 m/s, 35° up: landing, peak and arc

math.calculus · math.trig · data.charts

v = 20; a = 35 deg; g = 9.81
y = |t| v * sin(a) * t - g / 2 * t ** 2
land = root(y, 0.1, 10)
peak = root(|t| deriv(y, t), 0, land)
{seconds: round(land, 2), meters: round(v * cos(a) * land, 1), peak: round(y(peak), 2), arc: (0..=30).map(|i| y(land * i / 30)).sparkline}
# → {seconds: 2.34, meters: 38.3, peak: 6.71, arc: "▁▂▃▄▄▅▅▆▆▇▇█████████▇▇▆▆▅▅▄▄▃▂▁"}

fractions closing in on pi: convergents of its continued fraction, and how far off each is

math.numtheory · data.lists

terms = pi.cfrac(5)
fold = |ts| ts.reverse.drop(1).reduce(ts.last, |acc, t| t + 1 / acc)
(1..=5).map(|n| fold(terms.take(n))).map(|c| [c to frac, sci(abs(c - pi), 1)])
# → [[3, "1.4e-1"], [22/7, "1.3e-3"], [333/106, "8.3e-5"], [355/113, "2.7e-7"], [103993/33102, "5.8e-10"]]

units.kitchen · data.maps

cups = {flour: 2.25 cup, sugar: 0.75 cup, butter: 1 cup, honey: 2 tbsp}
cups.entries.map(|[k, v]| [k, round(v * 60 / 24 * density(k) to g)]).from_entries
# → {flour: 705 g, sugar: 377 g, butter: 568 g, honey: 105 g}

a Fermi estimate with honest error bars: piano tuners in Chicago

math.uncertainty · units

pianos = (2.7e6 ± 0.1e6) / (2.5 ± 0.5) * (5% ± 2%)    # people / household * share with a piano
per_tuner = (4 ± 1) / d * 250 d / yr
pianos / yr / per_tuner
# → 54 ± 17.4372

three receipts, three unknown prices: coffee, bagel, juice

math.linalg · units.money

# rows: [coffees, bagels, juices] on each receipt
solve([[2, 1, 0], [1, 2, 1], [0, 1, 2]], [$9.50, $11.50, $8.50])
# → [$3.50, $2.50, $3.00]

text, data and dev

how far apart were the errors in a log, and what were they?

text · time · math.stats · data.lists

log = "08:59:58 INFO start
09:00:03 ERROR db timeout
09:00:41 INFO retry
09:02:15 ERROR db timeout
09:07:50 ERROR disk full"
errors = log.grep("ERROR")
times = errors.map(|l| date("2026-10-06 " + l.match(r"^\S+")))
{gaps: times.deltas.map(|d| d.parts), kinds: errors.map(|l| l.split("ERROR ")[1]).count_by(|k| k)}
# → {gaps: ["2 min 12 s", "5 min 35 s"], kinds: {db timeout: 2, disk full: 1}}

crack a Caesar cipher: try every shift, keep the one with the most real words

text.ciphers · text.layout · data

secret = "wkh vhfuhw sdvvzrug lv vzruglvk"
common = ["the", "is", "and", "a", "of", "to", "secret", "password", "in"]
score = |s| s.words.filter(|w| w in common).len
(0..26).map(|k| secret.caesar(-k)).sort(score).last
# → "the secret password is swordish"

a running log as CSV; h:m:s times become durations by a dot product

fs · data.tables · math.linalg · units

runs = "date,km,time\n2026-09-01,5,0:27:30\n2026-09-04,10,0:58:10\n2026-09-08,5,0:26:05\n2026-09-12,21.1,2:05:40".from_csv
dur = |t| dot(t.split(":").map(int), [1 h, 1 min, 1 s])
pretty(runs.map(|r| {date: r.date, km: r.km, per_km: dur(r.time) / r.km to min}).table.sort_by("per_km"))
# →  #  date          km      per_km
# → ────────────────────────────────
# →  0  2026-09-08     5  5 min 13 s
# →  1  2026-09-01     5  5 min 30 s
# →  2  2026-09-04    10  5 min 49 s
# →  3  2026-09-12  21.1  5 min 57 s
# → ────────────────────────────────
# →  4 rows · 3 columns

carve a /22 into /24s and see which one each server lands in

dev.net · data.tables

servers = ["10.0.1.20", "10.0.3.7", "10.0.3.250", "10.0.0.9"]
pretty(subnets("10.0.0.0/22", 24).map(|b| {
  block: b, hosts: cidr(b).hosts, servers: servers.filter(|s| in_cidr(s, b)).join(" "),
}).table)
# →  #  block        hosts  servers
# → ───────────────────────────────────────────
# →  0  10.0.0.0/24    254  10.0.0.9
# →  1  10.0.1.0/24    254  10.0.1.20
# →  2  10.0.2.0/24    254  
# →  3  10.0.3.0/24    254  10.0.3.7 10.0.3.250
# → ───────────────────────────────────────────
# →  4 rows · 3 columns

six hues, each as light as it can be while still readable on white

dev.colors · data.lists

readable = |h| (20..=70).map(|l| hsl(h, 75, l)).filter(|c| contrast(c, "white") >= 4.5).last
(0..6).map(|i| readable(i * 60)).map(|c| "{swatch(c)} {round(contrast(c, "white"), 2)}").join("\n")
# →      #e12d2d 4.55
# →      #787811 4.67
# →      #138613 4.72
# →      #128181 4.68
# →      #6363e9 4.68
# →      #cd1dcd 4.51

when were this token and this ID minted, in Tokyo time?

dev · dev.ids · time.zones

token = jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZGEiLCJpYXQiOjE1MTYyMzkwMjJ9.c2ln")
id = uuid_info("01890a5d-ac96-774b-bcce-b302099a8057")
[token.payload.iat, id.timestamp].map(|d| [d to "Tokyo", d.relative])
# → [[2018-01-18 10:30:22 +09:00, "9 years ago"], [2023-06-30 12:34:18 +09:00, "3 years ago"]]

anagram groups: words that sort to the same letters

text.layout · data.lists

words = "listen silent enlist google tinsel inlets banana stone tones notes onset".words
words.group_by(|w| w.chars.sort.join).values.filter(|g| g.len > 1)
# → [["listen", "silent", "enlist", "tinsel", "inlets"], ["stone", "tones", "notes", "onset"]]

a spellchecker in one line: each word’s closest match in a dictionary

text.compare · text.layout

dict = "the quick brown fox jumps over lazy dog".words
"teh qiuck bronw fox jumsp ovr the lazzy dgo".words.map(|w| w.closest(dict)).join(" ")
# → "the quick brown fox jumps over the lazy dog"

newest release per major version, by semver precedence rather than string order

dev · data.lists · data.maps

tags = ["1.9.3", "1.10.0", "2.0.0-rc.1", "2.0.0", "1.2.11", "2.1.0-beta"]
tags.group_by(|t| semver(t).major).map_values(|vs| vs.sort(semver).last)
# → {1: "1.10.0", 2: "2.1.0-beta"}

how many files until two CRC-32s probably collide?

math.random · math.calculus

half = root(|k| birthday_paradox(round(k), 2 ** 32) - 0.5, 1, 1e6)
{even_odds_at: round(half), at_10k_files: odds(birthday_paradox(10000, 2 ** 32))}
# → {even_odds_at: 77164, at_10k_files: "1 in 86: about as likely as rolling double sixes (1 in 36)"}

proof of work: the first nonce whose SHA-256 starts with 000

text.hash · math

n = 0
while !"zil:{n}".sha256.starts_with("000") { n += 1 }
{nonce: n, hash: "zil:{n}".sha256[..16], expected_tries: 16 ** 3}
# → {nonce: 419, hash: "000fead2fe0984b8", expected_tries: 4096}

puzzles and play

prime dates: days of 2027 whose YYYYMMDD is prime

math.numtheory · time · data.lists

days = (0..365).map(|i| date(2027, 1, 1) + i * 1 d)
primes = days.filter(|d| is_prime(int(d.format("%Y%m%d"))))
{count: primes.len, first: primes.take(3).map(|d| d.format("%b %d")), by_month: primes.count_by(|d| d.format("%b"))}
# → {count: 19, first: ["Jan 19", "Jan 23", "Feb 03"], by_month: {Jan: 2, Feb: 1, Mar: 2, Apr: 1, May: 2, Jun: 2, Jul: 2, Aug: 2, Sep: 2, Nov: 3}}

numbers that are palindromes in both decimal and binary

math.bases · data.lists

pal = |n| n.digits == n.digits.reverse
(1..1000).filter(|n| pal(n) && pal(n to bin))
# → [1, 3, 5, 7, 9, 33, 99, 313, 585, 717]

which points are in the Mandelbrot set? escape step, or stays

math.complex · data.maps

escapes = fn(c) { z = 0; for n in 1..=50 { z = z * z + c; if abs(z) > 2 { return n } }; return "stays" }
[0, -1, 1, 1i, 0.3 + 0.5i, -0.75 + 0.1i].map(|c| [str(c), escapes(c)]).from_entries
# → {0: "stays", -1: "stays", 1: 3, 1i: "stays", 0.3 + 0.5i: "stays", -0.75 + 0.1i: 33}

the exact chance of a poker full house

math.numtheory · math.random

p = 13 * choose(4, 3) * 12 * choose(4, 2) / choose(52, 5)
[p to frac, odds(p)]
# → [6/4165, "1 in 694: about as likely as flipping ten heads in a row (1 in 1,024)"]

files, shell and network (shown, not run)

disk usage by file type, as a bar chart

fs · data.lists · data.maps · data.charts

files = glob("**/*").filter(|f| f.type == "file")
files.group_by(|f| f.name.ext).map_values(|fs| fs.map(|f| f.size).sum to KB).bars

which weekdays you commit on

sys · time · data.lists · data.charts

sh("git log --format=%ad --date=short").lines.count_by(|d| date(d).weekday).bars

PATH entries that point nowhere

sys · fs

sep = if env("OS") == "Windows_NT" { ";" } else { ":" }
env("PATH").split(sep).filter(|p| !exists(p))

files with the most TODOs

fs · text · data.tables

todos = glob("src/**/*.rs").map(|f| {file: f.name, todos: read_file(f.name).grep("TODO").len})
todos.filter(|r| r.todos > 0).table.sort_by_desc("todos")

sort Downloads into a folder per month

fs · time

for f in ls("Downloads").where(|f| f.type == "file") {
  dir = path_join("Downloads", f.modified.format("%Y-%m"))
  mkdir(dir)
  mv(path_join("Downloads", f.name), path_join(dir, f.name))
}

a weight log: zil weigh.zil 71.2 adds today’s entry and charts them all

sys · fs · time · data.charts

if !exists("weight.csv") { write_file("weight.csv", "date,kg") }
append_file("weight.csv", "{today.format("%Y-%m-%d")},{args()[0]}")
read_file("weight.csv").from_csv.kg.sparkline

describe any CSV column: zil stats.zil data.csv price

sys · fs · math.stats

[file, col] = args()
read_file(file).from_csv.map(|r| r[col]).describe

GitHub stars, straight from the API

sys · fs

["rust-lang/rust", "zanbowie138/zil"].map(|r| fetch("https://api.github.com/repos/{r}").from_json.stargazers_count)

the weather in Oslo, in Fahrenheit

sys · fs · units

now_oslo = fetch("https://wttr.in/Oslo?format=j1").from_json.current_condition[0]
{temp: int(now_oslo.temp_C) * 1 C to F, feels_like: int(now_oslo.FeelsLikeC) * 1 C to F, sky: now_oslo.weatherDesc[0].value}

a 9-day trip at $150 a day, in local money (live rates)

sys · units.money · geo

allow_network_access()
{japan: $150 * 9 to "Japan", vietnam: $150 * 9 to "Vietnam", mexico: $150 * 9 to "Mexico"}

one phrase, three languages

text.translation · sys

allow_network_access()
["es", "ja", "de"].map(|l| "where is the train station?".translate(l))

core

values, printing, type conversions, parsing, help

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

types

# nil
nil
# bool
true
# int
0xff
# float
1.5e3
# → 1500
# frac
7/2 to frac
# → 7/2
# fn
|x| x * 2
# → <fn>

names

# input  (stdin as a string): input.lines.len

conversions

# to str int float frac bool list
"42" to int
# → 42

pretty

# long: thousands separators, decimals kept
pretty(1234567.891)
# → "1,234,567.891"
# short: K M B T, 3 significant figures
pretty(1234567, "short")
# → "1.23M"

Functions

functiondescription
print(a?: any, ...)print values separated by spaces
page(a?: any, ...)print like print, but long output opens a scrollable pager (arrows, wheel, / to search, q to quit) when shown on a terminal
type(v: any)the type name of a value
str(v: any)convert to a string (full float precision)
int(v: num|str) / int(s: str, base: int)convert to an integer, parsing strings in an optional base
float(v: num|quantity|str)convert to a float; drops a quantity’s unit
frac(v: num)show as a fraction; floats become the simplest fraction within 1e-12
bool(v: any)truthiness: false only for nil and false
list(v: str|list|map|set|table)convert to a list: characters, a copy, [key, value] pairs, a set’s items, or a table’s rows as maps
pretty(v: any) / pretty(v: any, style: str)human-readable text; style is “long” (default) or “short”; help(int), help(date), help(set), help(table) and the quantity, uncertain, list and map module pages show each type’s rules
parse(s: str)read a zil literal (number, string, list, map, quantity); never runs code
help(topic?: any)this help, as text; topic is a function, module (“trig” or “math.trig”), unit, or any value to list functions for its type
tip()a random one-liner or recipe to try, from help(“examples”) and help(“advanced”)

print

print(a?: any, ...): print values separated by spaces

print("total:", 5 km)

See also: str

page

page(a?: any, ...): print like print, but long output opens a scrollable pager (arrows, wheel, / to search, q to quit) when shown on a terminal

help("math") |> page
(1..200).map(|n| n ** 2).join("\n") |> page

See also: print, help

type

type(v: any): the type name of a value

type(5 km)
# → "quantity"
type("hi")
# → "str"
type([1])
# → "list"

See also: str, int, float

str

str(v: any): convert to a string (full float precision)

str(1/3)
# → "0.3333333333333333"
str(5 km)
# → "5 km"

See also: int, float

int

int(v: num|str) / int(s: str, base: int): convert to an integer, parsing strings in an optional base

int(3.9)
# → 3
"ff".int(16)
# → 0xff
"0b101".int
# → 0b101

See also: float, str

float

float(v: num|quantity|str): convert to a float; drops a quantity’s unit

float("2.5")
# → 2.5
float(5 km)
# → 5

See also: int, str

frac

frac(v: num): show as a fraction; floats become the simplest fraction within 1e-12

7/2 to frac
# → 7/2
1/3 + 1/6 to frac
# → 1/2
0.75.frac
# → 3/4

See also: float

bool

bool(v: any): truthiness: false only for nil and false

bool(0)
# → true
nil to bool
# → false

See also: str

list

list(v: str|list|map|set|table): convert to a list: characters, a copy, [key, value] pairs, a set’s items, or a table’s rows as maps

list("abc")
# → ["a", "b", "c"]
"abc" to list
# → ["a", "b", "c"]
{a: 1, b: 2}.list
# → [["a", 1], ["b", 2]]

See also: chars, parse

pretty

pretty(v: any) / pretty(v: any, style: str): human-readable text; style is “long” (default) or “short”; help(int), help(date), help(set), help(table) and the quantity, uncertain, list and map module pages show each type’s rules

pretty(1234567)
# → "1,234,567"
pretty(1234567, "short")
# → "1.23M"

See also: str, commas, simplify, parts, format

parse

parse(s: str): read a zil literal (number, string, list, map, quantity); never runs code

"[1, 2.5, 0xff]".parse
# → [1, 2.5, 0xff]
"5 km".parse to m
# → 5000 m

See also: str, nums

help

help(topic?: any): this help, as text; topic is a function, module (“trig” or “math.trig”), unit, or any value to list functions for its type

help(upper)
help("math.trig")
help(today)
help("text") |> grep("case")

tip

tip(): a random one-liner or recipe to try, from help(“examples”) and help(“advanced”)

tip()

See also: help

More examples

core

# type of anything
type(5 km)
# → "quantity"
# parse values from text
parse("[1, 2, 5 km]")
# → [1, 2, 5 km]

fs

files, directories, paths, JSON and CSV

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

tables

# ls(dir?), glob(pat) give name, type, size, modified
# glob: * and ? within a name, ** across directories

Functions

functiondescription
read_file(path: str)file contents as a string
write_file(path: str, v: any)replace a file with v as text; a list is one item per line, a table is CSV
append_file(path: str, v: any)add v to the end of a file as text, creating it if needed; a list is one item per line
exists(path: str)whether a file or directory exists
is_dir(path: str)whether path is a directory
ls(dir?: str)a directory’s entries as a table: name, type, size, modified
glob(pat: str)paths matching pat as a table like ls; * and ? match within a name, ** across directories
file_size(path: str)size in bytes, as a quantity
mtime(path: str)when the file was last modified
rm(path: str)delete a file or an empty directory
mv(from: str, to: str)move or rename a file or directory, creating missing folders
cp(from: str, to: str)copy a file, creating missing folders
mkdir(path: str)create a directory and any missing parents
path_join(a: str, b: str, ...)join path pieces with the OS separator
basename(path: str)the last piece of a path, or nil
dirname(path: str)everything before the last piece, or nil
ext(path: str)the extension without its dot, or nil
stem(path: str)the last piece without its extension, or nil
abspath(path: str)path made absolute from the current directory; need not exist
cwd()the current directory
cd(dir?: str)change the current directory, home by default; returns the new one
from_json(s: str)parse JSON: objects become maps, arrays lists
to_json(v: any)v as JSON; values JSON lacks (quantities, fractions, dates) become strings
from_csv(s: str, header?: bool)parse CSV; with a header row (the default) a table, else a list of rows. Numbers become numbers, empty cells nil
to_csv(rows: table|list)CSV text from a table, a list of maps, or a list of lists

read_file

read_file(path: str): file contents as a string

read_file("notes.txt").lines.len
read_file("data.json").from_json

See also: write_file, lines, from_csv

write_file

write_file(path: str, v: any): replace a file with v as text; a list is one item per line, a table is CSV

write_file("out.txt", [1, 2, 3])
write_file("files.csv", ls())

See also: read_file, append_file

append_file

append_file(path: str, v: any): add v to the end of a file as text, creating it if needed; a list is one item per line

append_file("log.txt", ["started {now}"])

See also: write_file

exists

exists(path: str): whether a file or directory exists

exists("Cargo.toml")

See also: is_dir

is_dir

is_dir(path: str): whether path is a directory

is_dir("src")

See also: exists

ls

ls(dir?: str): a directory’s entries as a table: name, type, size, modified

ls()
ls("src").where(|f| f.type == "file").name

See also: glob, sort_by

glob

glob(pat: str): paths matching pat as a table like ls; * and ? match within a name, ** across directories

glob("**/*.rs").size.sum
glob("*.csv").name

See also: ls

file_size

file_size(path: str): size in bytes, as a quantity

file_size("Cargo.lock") to KiB

See also: mtime, human_bytes

mtime

mtime(path: str): when the file was last modified

now - mtime("Cargo.lock") to h

See also: file_size

rm

rm(path: str): delete a file or an empty directory

rm("out.txt")

See also: mv, mkdir

mv

mv(from: str, to: str): move or rename a file or directory, creating missing folders

mv("draft.txt", "final.txt")

See also: cp, rm

cp

cp(from: str, to: str): copy a file, creating missing folders

cp("data.csv", "backup/data.csv")

See also: mv

mkdir

mkdir(path: str): create a directory and any missing parents

mkdir("out/2026/10")

See also: rm

path_join

path_join(a: str, b: str, ...): join path pieces with the OS separator

path_join("a", "b.txt")
# → "a/b.txt"

See also: dirname, basename

basename

basename(path: str): the last piece of a path, or nil

"a/b/c.txt".basename
# → "c.txt"

See also: dirname, stem

dirname

dirname(path: str): everything before the last piece, or nil

"a/b/c.txt".dirname
# → "a/b"

See also: basename

ext

ext(path: str): the extension without its dot, or nil

"a/b.tar.gz".ext
# → "gz"
"README".ext
# → nil

See also: stem

stem

stem(path: str): the last piece without its extension, or nil

"a/b.tar.gz".stem
# → "b.tar"

See also: ext, basename

abspath

abspath(path: str): path made absolute from the current directory; need not exist

abspath("src")

See also: cwd

cwd

cwd(): the current directory

cwd()

See also: abspath, cd

cd

cd(dir?: str): change the current directory, home by default; returns the new one

cd("src")
cd("..")
cd()

See also: cwd

from_json

from_json(s: str): parse JSON: objects become maps, arrays lists

"\{\"a\": [1, 2.5, null]}".from_json
# → {a: [1, 2.5, nil]}

See also: to_json, parse

to_json

to_json(v: any): v as JSON; values JSON lacks (quantities, fractions, dates) become strings

{a: [1, nil], b: 5 km}.to_json
# → "{\"a\":[1,null],\"b\":\"5 km\"}"

See also: from_json

from_csv

from_csv(s: str, header?: bool): parse CSV; with a header row (the default) a table, else a list of rows. Numbers become numbers, empty cells nil

"name,n\nx,1".from_csv
# → table([{name: "x", n: 1}])
"1,2\n3,4".from_csv(false)
# → [[1, 2], [3, 4]]

See also: to_csv, table

to_csv

to_csv(rows: table|list): CSV text from a table, a list of maps, or a list of lists

[{a: 1, b: "x,y"}].to_csv
# → a,b
# → 1,"x,y"
# → 
[[1, 2], [3, 4]].to_csv
# → 1,2
# → 3,4
# → 

See also: from_csv

More examples

fs

# JSON to a value
"\{\"a\": [1, 2]}".from_json.a.sum
# → 3
# CSV to a table
"name,n\nx,1\ny,2".from_csv.n.sum
# → 3
# path pieces
["a/b.tar.gz".stem, "a/b.tar.gz".ext]
# → ["b.tar", "gz"]

sys

script arguments, environment variables, shell commands, HTTP GET

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

scripts

# zil script.zil a b  →  args() is ["a", "b"]
# sh fails on a non-zero exit; run never does
# currency rates and translation stay offline until allow_network_access()

Functions

functiondescription
args()command-line arguments after the script (or after -e code), as strings
env() / env(name: str)an environment variable, or nil; with no name, all of them as a map
exit(code?: int)stop the program with an exit code (default 0)
sh(cmd: str)run a shell command (sh -c, or cmd /C on Windows) and return its output, trailing newline trimmed; errors on a non-zero exit
run(cmd: str)run a shell command and return {code, out, err}; never fails on the exit code
fetch(url: str)HTTP GET a URL and return the body as a string; errors on a non-2xx status
allow_network_access()let currency rates and translation go online for the rest of the session; until then they only use cached data

args

args(): command-line arguments after the script (or after -e code), as strings

args()
# → []

See also: env

env

env() / env(name: str): an environment variable, or nil; with no name, all of them as a map

env("ZIL_SURELY_UNSET")
# → nil

See also: args

exit

exit(code?: int): stop the program with an exit code (default 0)

exit(1)

See also: args

sh

sh(cmd: str): run a shell command (sh -c, or cmd /C on Windows) and return its output, trailing newline trimmed; errors on a non-zero exit

sh("git rev-parse --short HEAD")
sh("ls").lines.len

See also: run

run

run(cmd: str): run a shell command and return {code, out, err}; never fails on the exit code

run("git status").code

See also: sh

fetch

fetch(url: str): HTTP GET a URL and return the body as a string; errors on a non-2xx status

fetch("https://api.github.com/repos/rust-lang/rust").from_json.stargazers_count

See also: from_json

allow_network_access

allow_network_access(): let currency rates and translation go online for the rest of the session; until then they only use cached data

allow_network_access()

See also: fetch

More examples

sys

# arguments after the script
args()
# → []
# is a variable set?
env("ZIL_SURELY_UNSET") == nil
# → true
# how many env vars
env().len > 0
# → true

text

case, splitting, search, regex; encodings, ciphers and more below

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

types

# str
"héllo\tworld"
# regex
r"\d+"

operators

# str + v
"v" + 2
# → "v2"
# str * int
"ab" * 3
# → "ababab"

Submodules

moduleabout
layoutpad, center, wrap, truncate, dedent; snake_case, camelCase, slugs; words
compareedit distance, similarity, the closest of a list, line-by-line diffs
translationtranslate text between languages online; cached translations work offline
encodingbase64, base32, base58, URL and hex encodings, UTF-8 bytes, hex dumps
hashSHA-1/256/512, MD5, HMAC and CRC-32 of strings
ciphersCaesar, ROT13, Atbash, Vigenère, Morse, Braille, NATO spelling
fancymock case, l33t, uwu, Pig Latin, zalgo, upside-down, 𝐛𝐨𝐥𝐝/𝒮𝒸𝓇𝒾𝓅𝓉/wide
placeholderlorem ipsum placeholder text, random team and person names

Functions

functiondescription
upper(s: str)uppercase a string
lower(s: str)lowercase a string
trim(s: str)strip leading and trailing whitespace
capitalize(s: str)uppercase the first character
split(s: str, sep?: str|regex)split on sep (string or regex); whitespace if omitted
lines(s: str)split into lines
chars(s: str)list of characters
join(xs: list, sep?: str)join items into a string
replace(s: str, pat: str|regex, with: str)replace all matches; regex replacements can use $1
starts_with(s: str, prefix: str)whether s starts with prefix
ends_with(s: str, suffix: str)whether s ends with suffix
match(s: str, re: regex)first match, its capture groups, or nil
find_all(s: str, pat: str|regex)list of all matches
grep(v: str|list, pat: str|regex)lines of a string (or items of a list) containing pat
repeat(s: str, n: int)repeat a string n times
nums(s: str)every number in a string
strip_ansi(s: str)remove terminal color and cursor codes

upper

upper(s: str): uppercase a string

"hello".upper
# → "HELLO"
upper("zil")
# → "ZIL"

See also: lower, capitalize

lower

lower(s: str): lowercase a string

"HeLLo".lower
# → "hello"

See also: upper, capitalize

trim

trim(s: str): strip leading and trailing whitespace

"  hi  ".trim
# → "hi"

See also: split

capitalize

capitalize(s: str): uppercase the first character

"hello world".capitalize
# → "Hello world"

See also: upper

split

split(s: str, sep?: str|regex): split on sep (string or regex); whitespace if omitted

"a b  c".split
# → ["a", "b", "c"]
"x,y;z".split(r"[,;]")
# → ["x", "y", "z"]

See also: join, lines, chars

lines

lines(s: str): split into lines

"a\nb".lines
# → ["a", "b"]

See also: split

chars

chars(s: str): list of characters

"abc".chars
# → ["a", "b", "c"]

See also: split

join

join(xs: list, sep?: str): join items into a string

["a", "b"].join("-")
# → "a-b"
[1, 2].join
# → "12"

See also: split

replace

replace(s: str, pat: str|regex, with: str): replace all matches; regex replacements can use $1

"a.b".replace(".", "-")
# → "a-b"
"a  b   c".replace(r"\s+", " ")
# → "a b c"

See also: find_all, match

starts_with

starts_with(s: str, prefix: str): whether s starts with prefix

"abc".starts_with("a")
# → true

See also: ends_with, contains

ends_with

ends_with(s: str, suffix: str): whether s ends with suffix

"file.zil".ends_with(".zil")
# → true

See also: starts_with

match

match(s: str, re: regex): first match, its capture groups, or nil

"2026-10-06".match(r"(\d+)-(\d+)")
# → ["2026", "10"]
"id 42".match(r"\d+")
# → "42"

See also: find_all, replace

find_all

find_all(s: str, pat: str|regex): list of all matches

"a1b22c333".find_all(r"\d+")
# → ["1", "22", "333"]

See also: match, count

grep

grep(v: str|list, pat: str|regex): lines of a string (or items of a list) containing pat

"ok\nERROR 1\nERROR 2".grep("ERROR")
# → ["ERROR 1", "ERROR 2"]
["a1", "b", "c22"].grep(r"\d")
# → ["a1", "c22"]

See also: lines, filter, contains

repeat

repeat(s: str, n: int): repeat a string n times

"ab".repeat(3)
# → "ababab"

nums

nums(s: str): every number in a string

"x=3, y=-2.5; 1e3".nums
# → [3, -2.5, 1000]

See also: find_all, parse

strip_ansi

strip_ansi(s: str): remove terminal color and cursor codes

gradient("ab", "red", "blue").strip_ansi
# → "ab"

See also: rainbow, len

More examples

text

# numbers out of text
"a1b22c333".nums.sum
# → 356
# regex captures
"2026-10-06".match(r"(\d+)-(\d+)-(\d+)")
# → ["2026", "10", "06"]
# regex replace with $1
"CamelCaseName".replace(r"(\B[A-Z])", " $1")
# → "Camel Case Name"
# grep lines
"ok\nERROR disk full\nok".grep(r"ERROR")
# → ["ERROR disk full"]
# initials
"Ada Lovelace".split.map(|w| w[0]).join
# → "AL"
# printf
format("%-6s|%5.2f", "pi", pi)
# → "pi    | 3.14"

text.layout

pad, center, wrap, truncate, dedent; snake_case, camelCase, slugs; words

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
pad(s: any, width: int, fill?: str)pad on the right to width characters (left-align)
pad_left(s: any, width: int, fill?: str)pad on the left to width characters (right-align); numbers work too
center(s: any, width: int, fill?: str)center in width characters; extra fill goes right
truncate(s: str, n: int)cut to at most n characters, ending in … when cut
wrap(s: str, width: int)word-wrap each paragraph to width columns
dedent(s: str)remove the indentation every non-blank line shares
title(s: str)Capitalize Each Word
snake(s: str)snake_case; splits on spaces, punctuation and camelCase humps
camel(s: str)camelCase
kebab(s: str)kebab-case
slug(s: str)a lowercase URL slug: letters and digits joined by -
words(s: str)the words, without punctuation (apostrophes stay)
word_count(s: str)how many words

pad

pad(s: any, width: int, fill?: str): pad on the right to width characters (left-align)

"ab".pad(5) + "|"
# → "ab   |"
"ab".pad(5, ".")
# → "ab..."

See also: pad_left, center

pad_left

pad_left(s: any, width: int, fill?: str): pad on the left to width characters (right-align); numbers work too

42.pad_left(6, "0")
# → "000042"

See also: pad, center

center

center(s: any, width: int, fill?: str): center in width characters; extra fill goes right

"hi".center(8, "*")
# → "***hi***"

See also: pad, pad_left

truncate

truncate(s: str, n: int): cut to at most n characters, ending in … when cut

"a long sentence here".truncate(10)
# → "a long se…"

See also: wrap

wrap

wrap(s: str, width: int): word-wrap each paragraph to width columns

"the quick brown fox jumps over the lazy dog".wrap(15)
# → the quick brown
# → fox jumps over
# → the lazy dog

See also: truncate, dedent

dedent

dedent(s: str): remove the indentation every non-blank line shares

"    a\n      b\n    c".dedent
# → a
# →   b
# → c

See also: trim, wrap

title

title(s: str): Capitalize Each Word

"the lord of the rings".title
# → "The Lord Of The Rings"

See also: capitalize, snake

snake

snake(s: str): snake_case; splits on spaces, punctuation and camelCase humps

"parseHTTPRequest".snake
# → "parse_http_request"
"Total Price".snake
# → "total_price"

See also: camel, kebab

camel

camel(s: str): camelCase

"user_id_v2".camel
# → "userIdV2"

See also: snake, kebab

kebab

kebab(s: str): kebab-case

"MyComponentName".kebab
# → "my-component-name"

See also: snake, slug

slug

slug(s: str): a lowercase URL slug: letters and digits joined by -

"Hello, World! It's 2026".slug
# → "hello-world-its-2026"

See also: kebab

words

words(s: str): the words, without punctuation (apostrophes stay)

"Hello, world! Don't panic.".words
# → ["Hello", "world", "Don't", "panic"]

See also: word_count, split

word_count

word_count(s: str): how many words

"Hello, world! Don't panic.".word_count
# → 4

See also: words

More examples

layout

# a receipt line
"coffee".pad(12, ".") + "$4.50".pad_left(8)
# → "coffee......   $4.50"
# a banner
"menu".upper.center(20, "=")
# → "========MENU========"
# column name to variable
"Total Price (USD)".snake
# → "total_price_usd"
# blog URL
"Hello, World! It's 2026".slug
# → "hello-world-its-2026"
# reading time
(lorem(450).word_count / 200).ceil
# → 3

text.compare

edit distance, similarity, the closest of a list, line-by-line diffs

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
distance(a: str, b: str)edit distance: inserts, deletes, substitutions and swaps of neighbors to turn a into b
similarity(a: str, b: str)1 - distance / longer length: 1 is identical, 0 shares nothing
closest(s: str, options: list)the option with the smallest edit distance to s (ignoring case); nil for an empty list
text_diff(a: str, b: str)line diff: removed lines start with -, added with +, kept with two spaces

distance

distance(a: str, b: str): edit distance: inserts, deletes, substitutions and swaps of neighbors to turn a into b

distance("kitten", "sitting")
# → 3
distance("form", "from")
# → 1

See also: similarity, closest

similarity

similarity(a: str, b: str): 1 - distance / longer length: 1 is identical, 0 shares nothing

similarity("color", "colour")
# → 0.833333

See also: distance

closest

closest(s: str, options: list): the option with the smallest edit distance to s (ignoring case); nil for an empty list

"aple".closest(["apple", "maple", "ape"])
# → "apple"

See also: distance

text_diff

text_diff(a: str, b: str): line diff: removed lines start with -, added with +, kept with two spaces

text_diff("a\nb\nc", "a\nc\nd")
# →   a
# → - b
# →   c
# → + d

See also: distance

More examples

compare

# typo fixer
"recieve".closest(["deceive", "receive", "recipe"])
# → "receive"
# how alike
similarity("color", "colour").percent(0)
# → "83%"
# what changed
text_diff("a\nb\nc", "a\nB\nc\nd")
# →   a
# → - b
# → + B
# →   c
# → + d

text.translation

translate text between languages online; cached translations work offline

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

translate

# allow_network_access() first; cached translations work without it
# languages by code (es, de, zh-CN) or English name (spanish, german, chinese)
# from is auto-detected when left out

Functions

functiondescription
translate(text: str, to: str, from?: str)translate text into another language by code or English name; the source is detected when omitted

translate

translate(text: str, to: str, from?: str): translate text into another language by code or English name; the source is detected when omitted

"good morning".translate("es")
translate("Guten Morgen", "english", "german")

See also: allow_network_access

text.encoding

base64, base32, base58, URL and hex encodings, UTF-8 bytes, hex dumps

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

conversions

# to base64
"hi" to base64
# → "aGk="

Functions

functiondescription
base64(s: str)base64-encode a string; same as s to base64
encode(s: str, fmt: str)encode as “base64”, “base32”, “base58”, “url” or “hex”
decode(s: str, fmt: str)decode “base64”, “base32”, “base58”, “url” or “hex”; bytes that aren’t UTF-8 come back as a list
ord(c: str)Unicode code point of a single character
chr(n: int)character for a Unicode code point
bytes(s: str)list of the string’s UTF-8 bytes
from_bytes(xs: list)string from a list of UTF-8 bytes
byte_len(v: str|list)length in UTF-8 bytes rather than characters
hexdump(v: str|list)xxd-style dump of a string’s UTF-8 bytes or a list of bytes
entropy(v: str|list)Shannon entropy in bits per byte: 0 is constant, 8 is random

base64

base64(s: str): base64-encode a string; same as s to base64

base64("hi there")
# → "aGkgdGhlcmU="
"hi" to base64
# → "aGk="

See also: encode, decode

encode

encode(s: str, fmt: str): encode as “base64”, “base32”, “base58”, “url” or “hex”

"hi there".encode("base64")
# → "aGkgdGhlcmU="
"a b&c".encode("url")
# → "a%20b%26c"
"hi".encode("base32")
# → "NBUQ===="

See also: decode

decode

decode(s: str, fmt: str): decode “base64”, “base32”, “base58”, “url” or “hex”; bytes that aren’t UTF-8 come back as a list

"aGk=".decode("base64")
# → "hi"
"6869".decode("hex")
# → "hi"
"Cn8eVZg".decode("base58")
# → "hello"

See also: encode

ord

ord(c: str): Unicode code point of a single character

"A".ord
# → 65
"A".ord to hex
# → 0x41

See also: chr, bytes

chr

chr(n: int): character for a Unicode code point

97.chr
# → "a"
(65..70).map(chr).join
# → "ABCDE"

See also: ord

bytes

bytes(s: str): list of the string’s UTF-8 bytes

"hé".bytes
# → [104, 195, 169]

See also: from_bytes, ord

from_bytes

from_bytes(xs: list): string from a list of UTF-8 bytes

[104, 105].from_bytes
# → "hi"

See also: bytes, chr

byte_len

byte_len(v: str|list): length in UTF-8 bytes rather than characters

"héllo".byte_len
# → 6

See also: len, bytes

hexdump

hexdump(v: str|list): xxd-style dump of a string’s UTF-8 bytes or a list of bytes

hexdump("hi\n")
# → "00000000: 6869 0a                                  hi."
hexdump([0, 255, 65])
# → "00000000: 00ff 41                                  ..A"

See also: bytes, entropy

entropy

entropy(v: str|list): Shannon entropy in bits per byte: 0 is constant, 8 is random

"aaaa".entropy
# → 0
"abcd".entropy
# → 2

See also: hexdump

More examples

encoding

# base64
"hi" to base64
# → "aGk="
# URL encoding
"a b&c".encode("url")
# → "a%20b%26c"
# base58, as in Bitcoin
"hello".encode("base58")
# → "Cn8eVZg"
# UTF-8 bytes
"héllo".bytes
# → [104, 195, 169, 108, 108, 111]
# code point to char
chr(9731)
# → "☃"
# bytes vs characters
["😀".byte_len, "😀".len]
# → [4, 1]
# look inside a string
hexdump("héllo\tworld\n")
# → "00000000: 68c3 a96c 6c6f 0977 6f72 6c64 0a         h..llo.world."
# how random is it?
"aaaaaaaa".entropy
# → 0

text.hash

SHA-1/256/512, MD5, HMAC and CRC-32 of strings

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
sha1(s: str)hex SHA-1 hash
sha256(s: str)hex SHA-256 hash
sha512(s: str)hex SHA-512 hash
md5(s: str)hex MD5 hash
hmac(s: str, key: str, alg?: str)hex HMAC of s; alg is “sha256” (default), “sha1”, “sha512” or “md5”
crc32(v: str|list)CRC-32 checksum (the zip/PNG one)

sha1

sha1(s: str): hex SHA-1 hash

"hello".sha1
# → "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"

See also: sha256

sha256

sha256(s: str): hex SHA-256 hash

"hello".sha256[..16]
# → "2cf24dba5fb0a30e"

See also: md5, hmac

sha512

sha512(s: str): hex SHA-512 hash

"hello".sha512[..16]
# → "9b71d224bd62f378"

See also: sha256

md5

md5(s: str): hex MD5 hash

"hello".md5
# → "5d41402abc4b2a76b9719d911017c592"

See also: sha256

hmac

hmac(s: str, key: str, alg?: str): hex HMAC of s; alg is “sha256” (default), “sha1”, “sha512” or “md5”

hmac("msg", "secret")
# → "fe4f9c418f683f034f6af90d1dd5b86ac0355dd96332c59cc74598d0736107f6"
hmac("msg", "secret", "sha1")
# → "ca3efb15cfaf530e0d771e5cbe707104f006b257"

See also: sha256

crc32

crc32(v: str|list): CRC-32 checksum (the zip/PNG one)

"hello".crc32
# → 907060870
"hello".crc32 to hex
# → 0x3610a686

See also: sha256, md5

More examples

hash

# hashes
"hello".md5
# → "5d41402abc4b2a76b9719d911017c592"
# checksum
"hello".crc32 to hex
# → 0x3610a686
# sign a message
hmac("msg", "secret")
# → "fe4f9c418f683f034f6af90d1dd5b86ac0355dd96332c59cc74598d0736107f6"

text.ciphers

Caesar, ROT13, Atbash, Vigenère, Morse, Braille, NATO spelling

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
caesar(s: str, shift: int)shift each letter by shift places; negative shifts decode
rot13(s: str)Caesar by 13; applying it twice gives the original
atbash(s: str)mirror the alphabet: a <-> z, b <-> y
vigenere(s: str, key: str)Caesar with a shift per letter from a key word
unvigenere(s: str, key: str)undo vigenere
morse(s: str)text to Morse code, or Morse (dots, dashes, / between words) back to text
braille(s: str)text to Grade 1 Braille cells, or Braille back to text
nato(s: str)spell with the NATO phonetic alphabet

caesar

caesar(s: str, shift: int): shift each letter by shift places; negative shifts decode

"hello".caesar(3)
# → "khoor"
"khoor".caesar(-3)
# → "hello"

See also: rot13, vigenere

rot13

rot13(s: str): Caesar by 13; applying it twice gives the original

"hello".rot13
# → "uryyb"

See also: caesar

atbash

atbash(s: str): mirror the alphabet: a <-> z, b <-> y

"wizard".atbash
# → "draziw"

See also: caesar

vigenere

vigenere(s: str, key: str): Caesar with a shift per letter from a key word

"attack at dawn".vigenere("lemon")
# → "lxfopv ef rnhr"

See also: unvigenere, caesar

unvigenere

unvigenere(s: str, key: str): undo vigenere

"lxfopv ef rnhr".unvigenere("lemon")
# → "attack at dawn"

See also: vigenere

morse

morse(s: str): text to Morse code, or Morse (dots, dashes, / between words) back to text

"sos".morse
# → "... --- ..."
".... .. / - .... . .-. .".morse
# → "HI THERE"

See also: braille

braille

braille(s: str): text to Grade 1 Braille cells, or Braille back to text

"hello world".braille
# → "⠓⠑⠇⠇⠕⠀⠺⠕⠗⠇⠙"
"⠓⠊".braille
# → "hi"

See also: morse, nato

nato

nato(s: str): spell with the NATO phonetic alphabet

"SOS".nato
# → "Sierra Oscar Sierra"
"b2b".nato
# → "Bravo Two Bravo"

See also: morse

More examples

ciphers

# SOS
"sos".morse
# → "... --- ..."
# and back
"... --- ...".morse
# → "SOS"
# spoilers
"the butler did it".rot13
# → "gur ohgyre qvq vg"
# crack a Caesar
(1..26).map(|k| "Wkh hdjoh odqgv".caesar(-k)).grep("eagle")
# → ["The eagle lands"]
# Vigenère round trip
"attack at dawn".vigenere("lemon").unvigenere("lemon")
# → "attack at dawn"
# Braille
"hello".braille
# → "⠓⠑⠇⠇⠕"
# spell it over the phone
"zil 2".nato
# → "Zulu India Lima / Two"

text.fancy

mock case, l33t, uwu, Pig Latin, zalgo, upside-down, 𝐛𝐨𝐥𝐝/𝒮𝒸𝓇𝒾𝓅𝓉/wide

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
mock(s: str)aLtErNaTiNg CaSe, for quoting someone sarcastically
leet(s: str)l33t sp34k
uwu(s: str)uwu-ify text
pig_latin(s: str)translate to Pig Latin, keeping capitals and punctuation
zalgo(s: str, marks?: int)Z̷a̸l̵g̶o̴ text: pile marks (default 3) onto every character
flip(s: str)turn text upside down
style(s: str, style: str)restyle letters and digits with Unicode look-alikes; style is one of styles()
styles()the styles for style()
emoji(name: str)an emoji by name, or nil
emojify(s: str)replace :name: codes with emoji; unknown codes stay

mock

mock(s: str): aLtErNaTiNg CaSe, for quoting someone sarcastically

"this is fine".mock
# → "tHiS iS fInE"

See also: upper, leet

leet

leet(s: str): l33t sp34k

"elite hacker".leet
# → "3l173 h4ck3r"

See also: mock

uwu

uwu(s: str): uwu-ify text

"hello friend, I really love this".uwu
# → "hewwo fwiend, I weawwy wuv this ^w^"

See also: mock

pig_latin

pig_latin(s: str): translate to Pig Latin, keeping capitals and punctuation

"Hello, string theory!".pig_latin
# → "Ellohay, ingstray eorythay!"

See also: caesar

zalgo

zalgo(s: str, marks?: int): Z̷a̸l̵g̶o̴ text: pile marks (default 3) onto every character

"he comes".zalgo
# → "h\u{325}\u{343}\u{368}e\u{356}\u{31e}\u{318} c\u{35d}\u{33c}\u{30a}o\u{35c}\u{32e}\u{320}m\u{356}\u{33c}\u{346}e\u{325}\u{364}\u{34b}s\u{320}\u{308}\u{338}"
"ok".zalgo(1)
# → "o\u{334}k\u{339}"

See also: flip

flip

flip(s: str): turn text upside down

"hello world".flip
# → "plɹoʍ ollǝɥ"

See also: reverse, zalgo

style

style(s: str, style: str): restyle letters and digits with Unicode look-alikes; style is one of styles()

"Hello".style("bold")
# → "𝐇𝐞𝐥𝐥𝐨"
"vaporwave".style("wide")
# → "vaporwave"

See also: styles, banner, fonts

styles

styles(): the styles for style()

styles()
# → ["bold", "italic", "bold_italic", "script", "fraktur", "double", "sans", "sans_bold", "mono", "wide", "circled", "small_caps", "strike", "underline"]

See also: style

emoji

emoji(name: str): an emoji by name, or nil

emoji("taco")
# → "🌮"
emoji("fire")
# → "🔥"

See also: emojify

emojify

emojify(s: str): replace :name: codes with emoji; unknown codes stay

":fire: deploy on friday :skull:".emojify
# → "🔥 deploy on friday 💀"

See also: emoji

More examples

fancy

# reply to a bad take
"tabs are better than spaces".mock
# → "tAbS aRe BeTtEr ThAn SpAcEs"
# every style
styles().map(|st| "Zil 2".style(st))
# → ["𝐙𝐢𝐥 𝟐", "𝑍𝑖𝑙 2", "𝒁𝒊𝒍 2", "𝒵𝒾𝓁 2", "ℨ𝔦𝔩 2", "ℤ𝕚𝕝 𝟚", "𝖹𝗂𝗅 𝟤", "𝗭𝗶𝗹 𝟮", "𝚉𝚒𝚕 𝟸", "Zil 2", "Ⓩⓘⓛ ②", "Zɪʟ 2", "Z\u{336}i\u{336}l\u{336} 2\u{336}", "Z\u{332}i\u{332}l\u{332} 2\u{332}"]
# table flip
"(╯°□°)╯ " + "zil".flip
# → "(╯°□°)╯ lᴉz"
# a commit message
":rocket: ship it :tada:".emojify
# → "🚀 ship it 🎉"

text.placeholder

lorem ipsum placeholder text, random team and person names

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
lorem(words?: int)the classic lorem ipsum paragraph, or its first n words (repeating past the end)
team_name()a random team name in one of several styles, like “The Crimson Otters”, “Taco Squad” or “Lagos Lobsters”
person_name()a random first and last name

lorem

lorem(words?: int): the classic lorem ipsum paragraph, or its first n words (repeating past the end)

lorem(5)
# → "Lorem ipsum dolor sit amet."

See also: fortune

team_name

team_name(): a random team name in one of several styles, like “The Crimson Otters”, “Taco Squad” or “Lagos Lobsters”

team_name()
# → "Spicy Flamingos FC"

See also: person_name

person_name

person_name(): a random first and last name

person_name()
# → "Taylor Papadopoulos"

See also: team_name

More examples

placeholder

# a heading
lorem(3)
# → "Lorem ipsum dolor."
# how long is it?
lorem().split.len
# → 69
# name the squad
team_name()
# → "The Dramatic Dragons"
# a test user
person_name()
# → "Sam Okonkwo"

data

sorting and searching any collection; lists, maps, tables, charts below

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

types

# list
[1, 2, 3]
# map
{a: 1, b: 2}

operators

# x in v
2 in [1, 2]
# → true

pretty

# long: one item per line, indented, items pretty, strings quoted
# short: one line, items short
pretty([1234567, "a", {b: 2500 g}], "short")
# → "[1.23M, \"a\", {b: 2.5 kg}]"

Submodules

moduleabout
listsranges, map/filter/reduce, building lists
mapskeys, values, lookups, merging and transforming maps
setsunique values in insertion order: union, intersection, difference
tablesrows under named columns: filter, pick columns, sort
chartssparklines, bar charts, histograms, plots, heatmaps, progress bars and trees

Functions

functiondescription
len(v: str|list|map|set)length of a string, list, map or set
contains(v: str|list|map|set, x: any)substring/regex in a string, item in a list or set, key in a map
find(v: str|list, x: any)index of the first match, or nil
count(v: str|list, x: any)number of matches in a string or list
reverse(v: str|list)reverse a string or list
sort(xs: list|set, key?: fn)sorted list, optionally by key function
sort_desc(xs: list|set, key?: fn)like sort, largest first
unique(xs: list)drop duplicates, keeping first occurrences
first(xs: list)first item, or nil
last(xs: list)last item, or nil
take(v: str|list, n: int)the first n items or characters
drop(v: str|list, n: int)everything after the first n items or characters
any(xs: list, f?: fn)true if f (or the item itself) is truthy for some item
all(xs: list, f?: fn)true if f (or the item itself) is truthy for every item

len

len(v: str|list|map|set): length of a string, list, map or set

"héllo".len
# → 5
[1, 2, 3].len
# → 3
{a: 1}.len
# → 1

contains

contains(v: str|list|map|set, x: any): substring/regex in a string, item in a list or set, key in a map

"price: $12".contains(r"\$\d+")
# → true
[1, 2].contains(2)
# → true

See also: find, starts_with

find

find(v: str|list, x: any): index of the first match, or nil

"hello".find("l")
# → 2
[5, 6].find(6)
# → 1
"abc".find("z")
# → nil

See also: contains, count

count

count(v: str|list, x: any): number of matches in a string or list

"banana".count("a")
# → 3
[1, 2, 1].count(1)
# → 2

See also: find

reverse

reverse(v: str|list): reverse a string or list

"abc".reverse
# → "cba"
[1, 2, 3].reverse
# → [3, 2, 1]

See also: sort

sort

sort(xs: list|set, key?: fn): sorted list, optionally by key function

[3, 1, 2].sort
# → [1, 2, 3]
["ccc", "a", "bb"].sort(|w| w.len)
# → ["a", "bb", "ccc"]

See also: reverse, unique

sort_desc

sort_desc(xs: list|set, key?: fn): like sort, largest first

[3, 1, 2].sort_desc
# → [3, 2, 1]
["bb", "a", "ccc"].sort_desc(|w| w.len)
# → ["ccc", "bb", "a"]

See also: sort

unique

unique(xs: list): drop duplicates, keeping first occurrences

[1, 2, 1, 3].unique
# → [1, 2, 3]

See also: sort, count

first

first(xs: list): first item, or nil

[7, 8].first
# → 7

See also: last

last

last(xs: list): last item, or nil

[7, 8].last
# → 8

See also: first

take

take(v: str|list, n: int): the first n items or characters

[1, 2, 3].take(2)
# → [1, 2]
"hello".take(3)
# → "hel"

See also: drop, first

drop

drop(v: str|list, n: int): everything after the first n items or characters

[1, 2, 3].drop(2)
# → [3]
"hello".drop(3)
# → "lo"

See also: take, last

any

any(xs: list, f?: fn): true if f (or the item itself) is truthy for some item

[0, 5, 12].any(|x| x > 10)
# → true
[nil, false].any
# → false

See also: all, filter

all

all(xs: list, f?: fn): true if f (or the item itself) is truthy for every item

[2, 4, 6].all(|x| x % 2 == 0)
# → true
[].all
# → true

See also: any, filter

More examples

data

# unique, order kept
[3, 1, 3, 2, 1].unique
# → [3, 1, 2]
# sort quantities
[1 km, 900 m, 1 mi].sort
# → [900 m, 1 km, 1 mi]
# sort dates
[date("2026-12-25"), date("2026-01-01")].sort
# → [2026-01-01, 2026-12-25]

data.lists

ranges, map/filter/reduce, building lists

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

operators

# a..b
1..4
# → [1, 2, 3]
# a..=b
1..=4
# → [1, 2, 3, 4]
# list + list
[1] + [2, 3]
# → [1, 2, 3]

Functions

functiondescription
range(n: int) / range(a: int, b: int)integers in [0, n) or [a, b); same as a..b
push(xs: list, v: any)append v in place and return the list
pop(xs: list)remove and return the last item in place, or nil if empty
shift(xs: list)remove and return the first item in place, or nil if empty
unshift(xs: list, v: any)insert v at the front in place and return the list
map(xs: list, f: fn)apply f to every item
filter(xs: list, f: fn) / filter(m: map, f: fn)keep items where f is truthy; for maps, f gets (key, value)
reduce(xs: list, init: any, f: fn)fold with f(acc, item)
flatten(xs: list)unpack nested lists one level
zip(a: list, b: list)pair up items, stopping at the shorter list
enumerate(xs: list)[index, item] pairs
group_by(xs: list, f: fn)map from each key f gives to the items with that key
count_by(xs: list, f: fn)map from each key f gives to how many items have it
chunks(xs: list, n: int)split into lists of n items; the last may be shorter
windows(xs: list, n: int)every run of n neighboring items
step(xs: list, n: int)every nth item, starting with the first

range

range(n: int) / range(a: int, b: int): integers in [0, n) or [a, b); same as a..b

range(4)
# → [0, 1, 2, 3]
range(2, 5)
# → [2, 3, 4]

See also: map

push

push(xs: list, v: any): append v in place and return the list

[1, 2].push(3)
# → [1, 2, 3]

See also: pop, unshift

pop

pop(xs: list): remove and return the last item in place, or nil if empty

xs = [1, 2, 3]; [xs.pop, xs]
# → [3, [1, 2]]

See also: push, shift

shift

shift(xs: list): remove and return the first item in place, or nil if empty

xs = [1, 2, 3]; [xs.shift, xs]
# → [1, [2, 3]]

See also: unshift, pop

unshift

unshift(xs: list, v: any): insert v at the front in place and return the list

[2, 3].unshift(1)
# → [1, 2, 3]

See also: shift, push

map

map(xs: list, f: fn): apply f to every item

[1, 2, 3].map(|x| x * 10)
# → [10, 20, 30]

See also: filter, reduce

filter

filter(xs: list, f: fn) / filter(m: map, f: fn): keep items where f is truthy; for maps, f gets (key, value)

(1..10).filter(|x| x % 3 == 0)
# → [3, 6, 9]
{a: 1, b: 5}.filter(|k, v| v > 2)
# → {b: 5}

See also: map, reduce, filter_keys

reduce

reduce(xs: list, init: any, f: fn): fold with f(acc, item)

[1, 2, 3].reduce(10, |acc, x| acc + x)
# → 16

See also: sum, map

flatten

flatten(xs: list): unpack nested lists one level

[[1, 2], [3], 4].flatten
# → [1, 2, 3, 4]

See also: chunks

zip

zip(a: list, b: list): pair up items, stopping at the shorter list

zip([1, 2, 3], ["a", "b"])
# → [[1, "a"], [2, "b"]]

See also: enumerate

enumerate

enumerate(xs: list): [index, item] pairs

["a", "b"].enumerate
# → [[0, "a"], [1, "b"]]
["a", "b"].enumerate.map(|[i, x]| "{i}:{x}")
# → ["0:a", "1:b"]

See also: zip

group_by

group_by(xs: list, f: fn): map from each key f gives to the items with that key

["apple", "avocado", "banana"].group_by(|w| w[0])
# → {a: ["apple", "avocado"], b: ["banana"]}
(1..=6).group_by(|x| x % 2 == 0)
# → {false: [1, 3, 5], true: [2, 4, 6]}

See also: count_by

count_by

count_by(xs: list, f: fn): map from each key f gives to how many items have it

"mississippi".chars.count_by(|c| c)
# → {m: 1, i: 4, s: 4, p: 2}

See also: group_by, count

chunks

chunks(xs: list, n: int): split into lists of n items; the last may be shorter

(1..=7).chunks(3)
# → [[1, 2, 3], [4, 5, 6], [7]]

See also: windows, flatten

windows

windows(xs: list, n: int): every run of n neighboring items

[1, 2, 3, 4].windows(2)
# → [[1, 2], [2, 3], [3, 4]]
[1, 4, 9, 16].windows(2).map(|[a, b]| b - a)
# → [3, 5, 7]

See also: chunks

step

step(xs: list, n: int): every nth item, starting with the first

(0..=20).step(5)
# → [0, 5, 10, 15, 20]
(1..10).step(2)
# → [1, 3, 5, 7, 9]

See also: range

More examples

lists

# stepped range
(1..=10).step(3)
# → [1, 4, 7, 10]
# primes under 100
(1..100).filter(is_prime).len
# → 25
# pipe into a function
(1..=10).map(|x| x ** 2) |> sum
# → 385
# fold
[1, 2, 3].reduce(10, |acc, x| acc + x)
# → 16

data.maps

keys, values, lookups, merging and transforming maps

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

operators

# map + map
{a: 1} + {a: 2, b: 3}
# → {a: 2, b: 3}

Functions

functiondescription
keys(m: map)list of map keys
values(m: map)list of map values
get(m: map, k: any, default?: any)value at k, or default (nil if not given)
has(m: map, k: any)true if m has the key k
del(m: map, k: any)remove k in place and return the map
merge(a: map, b: map)new map with both; b wins on shared keys, same as a + b
entries(m: map)[key, value] pairs, same as list(m)
from_entries(xs: list)map from [key, value] pairs; later keys win
invert(m: map) / invert(c: str|list)swap a map’s keys and values (values become string keys), or the opposite color
map_values(m: map, f: fn)apply f to every value, keeping keys
filter_keys(m: map, f: fn)keep entries whose key f is truthy for

keys

keys(m: map): list of map keys

{a: 1, b: 2}.keys
# → ["a", "b"]

See also: values

values

values(m: map): list of map values

{a: 1, b: 2}.values
# → [1, 2]

See also: keys

get

get(m: map, k: any, default?: any): value at k, or default (nil if not given)

{a: 1}.get("a")
# → 1
{a: 1}.get("b", 0)
# → 0

See also: has

has

has(m: map, k: any): true if m has the key k

{a: 1}.has("a")
# → true

See also: get, contains

del

del(m: map, k: any): remove k in place and return the map

{a: 1, b: 2}.del("a")
# → {b: 2}

See also: merge

merge

merge(a: map, b: map): new map with both; b wins on shared keys, same as a + b

merge({a: 1, b: 2}, {b: 3})
# → {a: 1, b: 3}

See also: del

entries

entries(m: map): [key, value] pairs, same as list(m)

{a: 1, b: 2}.entries
# → [["a", 1], ["b", 2]]

See also: from_entries, list

from_entries

from_entries(xs: list): map from [key, value] pairs; later keys win

[["a", 1], ["b", 2]].from_entries
# → {a: 1, b: 2}
zip(["x", "y"], [1, 2]).from_entries
# → {x: 1, y: 2}

See also: entries

invert

invert(m: map) / invert(c: str|list): swap a map’s keys and values (values become string keys), or the opposite color

{a: 1, b: 2}.invert
# → {1: "a", 2: "b"}
invert("navy")
# → "#ffff7f"

See also: entries, grayscale

map_values

map_values(m: map, f: fn): apply f to every value, keeping keys

{a: 1, b: 2}.map_values(|v| v * 10)
# → {a: 10, b: 20}

See also: map, filter

filter_keys

filter_keys(m: map, f: fn): keep entries whose key f is truthy for

{a: 1, bb: 2}.filter_keys(|k| k.len > 1)
# → {bb: 2}

See also: filter

More examples

maps

# sum a map
{a: 1, b: 2}.values.sum
# → 3
# as [key, value] pairs
{a: 1, b: 2}.list
# → [["a", 1], ["b", 2]]
# defaults, overridden
{color: "red", size: 1} + {size: 3}
# → {color: "red", size: 3}
# loop over entries
out = []; for [k, v] in {a: 1, b: 2}.entries { out.push("{k}={v}") }; out
# → ["a=1", "b=2"]

data.sets

unique values in insertion order: union, intersection, difference

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

types

# set
set(1, 2, 3)

operators

# a | b  union
set(1, 2) | set(2, 3)
# → set(1, 2, 3)
# a & b  intersection
set(1, 2) & set(2, 3)
# → set(2)
# a - b  difference
set(1, 2) - set(2, 3)
# → set(1)
# a ^ b  symmetric difference
set(1, 2) ^ set(2, 3)
# → set(1, 3)
# x in s
2 in set(1, 2)
# → true

holds

# int, str, bool and nil only

pretty

# long: one item per line, indented, items pretty
# short: one line, items short
pretty(set(1500, "a"), "short")
# → "set(1.5K, \"a\")"

Functions

functiondescription
set(xs: list|str|set) / set(a?: any, ...)a set of a list’s items (or a string’s characters), or of the arguments
is_subset(a: set, b: set)true if every item of a is in b
is_superset(a: set, b: set)true if a has every item of b
is_disjoint(a: set, b: set)true if a and b share nothing
add(s: set, v: any)add v in place and return the set
remove(s: set, v: any)remove v in place (if there) and return the set

set

set(xs: list|str|set) / set(a?: any, ...): a set of a list’s items (or a string’s characters), or of the arguments

[3, 1, 3, 2].set
# → set(3, 1, 2)
set(1, 2)
"hello" to set
# → set("h", "e", "l", "o")

See also: unique, list

is_subset

is_subset(a: set, b: set): true if every item of a is in b

set(1, 2).is_subset(set(1, 2, 3))
# → true

See also: is_superset

is_superset

is_superset(a: set, b: set): true if a has every item of b

set(1, 2, 3).is_superset(set(1, 5))
# → false

See also: is_subset

is_disjoint

is_disjoint(a: set, b: set): true if a and b share nothing

set(1, 2).is_disjoint(set(3))
# → true

See also: is_subset

add

add(s: set, v: any): add v in place and return the set

set(1).add(2)
# → set(1, 2)

See also: remove, push

remove

remove(s: set, v: any): remove v in place (if there) and return the set

set(1, 2).remove(1)
# → set(2)

See also: add, del

More examples

sets

# distinct words
"the cat and the hat".split.set
# → set("the", "cat", "and", "hat")
# in both
set(1, 2, 3) & set(2, 3, 4)
# → set(2, 3)
# in either, not both
set(1, 2, 3) ^ set(2, 3, 4)
# → set(1, 4)
# order doesn't matter
set(1, 2) == set(2, 1)
# → true

data.tables

rows under named columns: filter, pick columns, sort

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

types

# table
[{a: 1, b: 2}] to table
# → table([{a: 1, b: 2}])

pretty

# long: the grid, cells pretty, strings bare, collections short
# short: one line, rows as maps, cells short
pretty([{a: 1234, b: "x"}].table, "short")
# → "table([{a: 1.23K, b: \"x\"}])"

access

# t.col (a column)
[{a: 1}, {a: 2}].table.a
# → [1, 2]
# t[i] (a row)
[{a: 1}, {a: 2}].table[1]
# → {a: 2}

Functions

functiondescription
table(rows: list|table)a table from a list of maps; columns are every key, missing cells nil
where(t: table, f: fn)keep rows where f(row) is truthy
select(t: table, col: str, ...)keep only these columns, in this order
reject(t: table, col: str, ...)drop these columns
sort_by(t: table, col: str)rows sorted by a column
sort_by_desc(t: table, col: str)rows sorted by a column, largest first

table

table(rows: list|table): a table from a list of maps; columns are every key, missing cells nil

[{a: 1}, {a: 2, b: 3}].table
# → table([{a: 1, b: nil}, {a: 2, b: 3}])
[{a: 1}] to table
# → table([{a: 1}])

See also: select, list

where

where(t: table, f: fn): keep rows where f(row) is truthy

[{n: 1}, {n: 5}].table.where(|r| r.n > 2)
# → table([{n: 5}])

See also: filter, sort_by

select

select(t: table, col: str, ...): keep only these columns, in this order

[{a: 1, b: 2, c: 3}].table.select("c", "a")
# → table([{c: 3, a: 1}])

See also: reject

reject

reject(t: table, col: str, ...): drop these columns

[{a: 1, b: 2, c: 3}].table.reject("b")
# → table([{a: 1, c: 3}])

See also: select

sort_by

sort_by(t: table, col: str): rows sorted by a column

[{n: 3}, {n: 1}].table.sort_by("n")
# → table([{n: 1}, {n: 3}])

See also: sort_by_desc, sort

sort_by_desc

sort_by_desc(t: table, col: str): rows sorted by a column, largest first

[{n: 1}, {n: 3}].table.sort_by_desc("n")
# → table([{n: 3}, {n: 1}])

See also: sort_by

More examples

tables

# a column as a list
[{n: 1}, {n: 5}].table.n
# → [1, 5]
# list fns see rows as maps
[{n: 1}, {n: 5}].table.map(|r| r.n * 2)
# → [2, 10]
# sort, pick, count
[{f: "a", kb: 3}, {f: "b", kb: 9}].table.sort_by_desc("kb").f
# → ["b", "a"]

data.charts

sparklines, bar charts, histograms, plots, heatmaps, progress bars and trees

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
sparkline(xs: list)a one-line chart: one block character per number, scaled min to max
bars(m: map|list, width?: int)a horizontal bar chart of a map (labels → numbers) or a list; width defaults to 40
histogram(xs: list, bins?: int)count numbers into equal-width bins (default 10) and draw them as bars
plot(f: fn, a: num, b: num)plot f from a to b in braille dots, 60 columns by 16 rows
tree_view(v: map|list)nested maps and lists drawn like the tree command
heatmap(rows: list)a list of rows of numbers as shades from ░ (low) to █ (high)
progress(frac: num, width?: int)a progress bar for frac from 0 to 1, width cells wide (default 30)

sparkline

sparkline(xs: list): a one-line chart: one block character per number, scaled min to max

[1, 5, 2, 8, 3].sparkline
# → "▁▅▂█▃"
(0..20).map(|x| sin(x / 3)).sparkline
# → "▅▆▇▇███▇▆▅▄▃▂▁▁▁▂▂▄▅"

See also: bars, plot

bars

bars(m: map|list, width?: int): a horizontal bar chart of a map (labels → numbers) or a list; width defaults to 40

bars({a: 3, b: 7, c: 5})
# → a │█████████████████▏ 3
# → b │████████████████████████████████████████ 7
# → c │████████████████████████████▋ 5
[2, 4, 8].bars(16)
# → 0 │████ 2
# → 1 │████████ 4
# → 2 │████████████████ 8

See also: histogram, sparkline

histogram

histogram(xs: list, bins?: int): count numbers into equal-width bins (default 10) and draw them as bars

[1, 2, 2, 3, 3, 3, 4, 4, 5].histogram(5)
# →   1–1.8 │█████████████▍ 1
# → 1.8–2.6 │██████████████████████████▋ 2
# → 2.6–3.4 │████████████████████████████████████████ 3
# → 3.4–4.2 │██████████████████████████▋ 2
# →   4.2–5 │█████████████▍ 1

See also: bars, count_by

plot

plot(f: fn, a: num, b: num): plot f from a to b in braille dots, 60 columns by 16 rows

plot(|x| x ** 2, -1, 1)
# →            1 ┤⠡⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠌
# →              ┤⠀⠡⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠌⠀
# →              ┤⠀⠀⠡⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠌⠀⠀
# →              ┤⠀⠀⠀⠡⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠌⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠡⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠌⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠐⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠂⠀⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠀⠐⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⠂⠀⠀⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠀⠀⠈⢂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡐⠁⠀⠀⠀⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠡⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠌⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →              ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠤⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# → 0.0000706165 ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠒⠢⠤⢄⣀⣀⣀⣀⡠⠤⠔⠒⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →               -1                                                         1

See also: sparkline

tree_view

tree_view(v: map|list): nested maps and lists drawn like the tree command

tree_view({src: {main: "rs", lib: "rs"}, docs: ["book"]})
# → ├── src
# → │   ├── main: rs
# → │   └── lib: rs
# → └── docs
# →     └── book

See also: keys, from_json

heatmap

heatmap(rows: list): a list of rows of numbers as shades from ░ (low) to █ (high)

[[1, 2, 3], [4, 5, 6], [7, 8, 9]].heatmap
# →   ░░░░
# → ▒▒▒▒▓▓
# → ▓▓████

See also: bars, sparkline

progress

progress(frac: num, width?: int): a progress bar for frac from 0 to 1, width cells wide (default 30)

progress(0.42)
# → "▕████████████▋                 ▏ 42%"
progress(2 / 3, 12)
# → "▕████████    ▏ 67%"

See also: bars

More examples

charts

# a trend in one line
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].sparkline
# → "▃▁▄▁▅█▂▅▅▃▅"
# a budget
bars({rent: $1800, food: $600, fun: $250})
# → rent │ $1,800.00
# → food │ $600.00
# →  fun │ $250.00
# letter frequencies
"mississippi".chars.count_by(|c| c) |> bars
# → m │██████████ 1
# → i │████████████████████████████████████████ 4
# → s │████████████████████████████████████████ 4
# → p │████████████████████ 2
# dice sums
(1..=2000).map(|_| rand(1, 6) + rand(1, 6)).histogram(11)
# →       2–2.90909 │█████▎ 45
# → 2.90909–3.81818 │████████████▏ 104
# → 3.81818–4.72727 │██████████████████▎ 157
# → 4.72727–5.63636 │█████████████████████████▎ 217
# → 5.63636–6.54545 │█████████████████████████████████▏ 285
# → 6.54545–7.45455 │████████████████████████████████████████ 344
# → 7.45455–8.36364 │██████████████████████████████████▏ 293
# → 8.36364–9.27273 │██████████████████████████▉ 231
# → 9.27273–10.1818 │███████████████████▋ 169
# → 10.1818–11.0909 │███████████▋ 100
# →      11.0909–12 │██████▍ 55
# a sine wave
plot(sin, 0, 2 * pi)
# →  0.999913 ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠒⠊⠉⠉⠉⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →           ┤⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →           ┤⠀⠀⠀⠀⠀⠀⠀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →           ┤⠀⠀⠀⠀⠀⠠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →           ┤⠀⠀⠀⠀⡐⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠡⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →           ┤⠀⠀⢀⠌⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →           ┤⠀⢀⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →           ┤⠠⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →           ┤⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠡⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠊
# →           ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⠁⠀
# →           ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡐⠁⠀⠀
# →           ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠌⠀⠀⠀⠀
# →           ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠂⠀⠀⠀⠀⠀
# →           ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠀⠀⠀⠀⠀⠀⠀
# →           ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀
# → -0.999913 ┤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⣀⣀⣀⡠⠤⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# →            0                                                    6.28319
# a multiplication table
(1..=6).map(|r| (1..=10).map(|c| r * c)).heatmap
# →                 ░░░░
# →         ░░░░░░░░░░░░
# →     ░░░░░░░░░░▒▒▒▒▒▒
# →     ░░░░░░▒▒▒▒▒▒▒▒▓▓
# →   ░░░░░░▒▒▒▒▒▒▓▓▓▓▓▓
# →   ░░░░▒▒▒▒▒▒▓▓▓▓████
# a config at a glance
tree_view({server: {host: "localhost", ports: [80, 443]}, debug: false})
# → ├── server
# → │   ├── host: localhost
# → │   └── ports
# → │       ├── 80
# → │       └── 443
# → └── debug: false

math

rounding, roots, logs, interpolation, constants; exact fractions and big ints

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

constants

# pi e tau phi inf nan

operators

# n%
20%
# → 0.2
# x + n%, x - n%
50 + 10%
# → 55
# n% of x
20% of 50
# → 10

Submodules

moduleabout
statssums, averages, spread and extremes of lists; units welcome
trigsine, cosine and friends; angles as units
numtheorygcd, primes, factoring, factorials, modular powers; exact at any size
bitscount, test, set, rotate and swap bits
baseshex, binary, octal and any base 2-36; digits of a number
formattingprintf and format specs, commas, numbers in words, ordinals, Roman numerals
randomrandom numbers and picks, dice, odds in plain words, fortunes, a magic 8-ball
uncertaintymeasurements with an error, 5 ± 0.1 m, carried through arithmetic
complexcomplex numbers, 2 + 3i; a number touching i is imaginary
linalgdot and cross products, matrix multiply, determinant, inverse, linear systems
calculusnumeric roots, derivatives and integrals of functions

Functions

functiondescription
sqrt(x: num|complex)square root
abs(x: num|quantity|complex)absolute value; keeps units. A complex number’s magnitude
round(x: num|quantity, digits?: int) / round(x: num|quantity, step: num|quantity)round to nearest; keeps units. A non-int second arg rounds to a multiple of it
sig(x: num|quantity, digits: int)round to that many significant figures; keeps units
floor(x: num|quantity)round down
ceil(x: num|quantity)round up
trunc(x: num|quantity)round toward zero; keeps units
sign(x: num|quantity)-1, 0 or 1
clamp(x: any, lo: any, hi: any)limit x to [lo, hi]
cbrt(x: num)cube root
exp(x: num|complex)e to the power x
ln(x: num|complex)natural log
log(x: num, base?: num)log base 10, or another base
hypot(x: num|quantity, y: num|quantity)sqrt(x² + y²) without overflow; keeps units
is_nan(x: any)whether x is nan (nan != nan)
lerp(a: any, b: any, t: num)a + (b - a) * t: the point a fraction t of the way from a to b; numbers, units or dates
remap(x: any, lo: any, hi: any, to_lo: any, to_hi: any)x moved from the range lo..hi to the range to_lo..to_hi, proportionally

sqrt

sqrt(x: num|complex): square root

sqrt(2)
# → 1.41421

See also: cbrt

abs

abs(x: num|quantity|complex): absolute value; keeps units. A complex number’s magnitude

abs(-3)
# → 3
abs(-2 km)
# → 2 km

See also: round, sign

round

round(x: num|quantity, digits?: int) / round(x: num|quantity, step: num|quantity): round to nearest; keeps units. A non-int second arg rounds to a multiple of it

round(pi, 2)
# → 3.14
round(2.5 km)
# → 3 km
round(7.3, 0.25)
# → 7.25
round(17 min, 15 min)
# → 15 min

See also: floor, ceil, trunc

sig

sig(x: num|quantity, digits: int): round to that many significant figures; keeps units

3.14159.sig(3)
# → 3.14
123456.sig(2)
# → 120000
0.0004567.sig(2)
# → 0.00046
(1 mi to m).sig(3)
# → 1610 m

See also: round, sci

floor

floor(x: num|quantity): round down

floor(2.7)
# → 2

See also: ceil, round

ceil

ceil(x: num|quantity): round up

ceil(2.1)
# → 3

See also: floor, round

trunc

trunc(x: num|quantity): round toward zero; keeps units

trunc(-2.7)
# → -2

See also: floor, round

sign

sign(x: num|quantity): -1, 0 or 1

sign(-5 km)
# → -1
sign(0)
# → 0

See also: abs

clamp

clamp(x: any, lo: any, hi: any): limit x to [lo, hi]

clamp(15, 0, 10)
# → 10
clamp(5 m, 1 m, 2 m)
# → 2 m

See also: min, max

cbrt

cbrt(x: num): cube root

cbrt(27)
# → 3

See also: sqrt

exp

exp(x: num|complex): e to the power x

exp(1)
# → 2.71828

See also: ln

ln

ln(x: num|complex): natural log

ln(e)
# → 1

See also: log, exp

log

log(x: num, base?: num): log base 10, or another base

log(1000)
# → 3
log(8, 2)
# → 3

See also: ln

hypot

hypot(x: num|quantity, y: num|quantity): sqrt(x² + y²) without overflow; keeps units

hypot(3, 4)
# → 5
hypot(3 m, 4 m)
# → 5 m

See also: sqrt, atan2

is_nan

is_nan(x: any): whether x is nan (nan != nan)

is_nan(nan)
# → true
is_nan(inf - inf)
# → true

lerp

lerp(a: any, b: any, t: num): a + (b - a) * t: the point a fraction t of the way from a to b; numbers, units or dates

lerp(10, 20, 0.25)
# → 12.5
lerp(0 C, 100 C, 0.37)
# → 37 C

See also: remap

remap

remap(x: any, lo: any, hi: any, to_lo: any, to_hi: any): x moved from the range lo..hi to the range to_lo..to_hi, proportionally

remap(5, 0, 10, 100, 200)
# → 150
remap(72 F, 32 F, 212 F, 0, 100)
# → 22.2222

See also: lerp, normalize

More examples

math

# exact fractions
(1..=6).map(|x| 1/x).sum to frac
# → 49/20
# decimals are exact
0.1 + 0.2 == 0.3
# → true
# 1/3 * 3 is exactly 1
1/3 * 3 == 1
# → true
# decimal to fraction
0.75 to frac
# → 3/4
# big ints never overflow
2 ** 100
# → 1267650600228229401496703205376
# add a percent
80 + 15%
# → 92
# percent of
15% of 64.50
# → 9.675
# halfway between dates
lerp(date("2026-01-01"), date("2026-12-31"), 1/2)
# → 2026-07-02
# °C to a 0-255 byte
remap(25, 0, 40, 0, 255).round
# → 159

math.stats

sums, averages, spread and extremes of lists; units welcome

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
sum(xs: list)add up a list; works with units
avg(xs: list)mean of a list
product(xs: list)multiply a list together
median(xs: list)middle value; mean of the middle two for an even count
mode(xs: list)most common item; the first one on ties
percentile(xs: list, p: num)the p-th percentile (0-100), interpolating between items
variance(xs: list)sample variance (n - 1)
stdev(xs: list)sample standard deviation (n - 1); works with units
min(xs: list) / min(a: any, b: any, ...)smallest value
max(xs: list) / max(a: any, b: any, ...)largest value
describe(xs: list)n, mean, stdev, min, quartiles and max in one map; units welcome
corr(xs: list, ys: list)Pearson correlation, from -1 to 1
fit(xs: list, ys: list)least-squares line: {slope, intercept, r2}
zscore(xs: list)how many standard deviations each item is from the mean
normalize(xs: list)rescale so the min is 0 and the max is 1
cumsum(xs: list)running totals
deltas(xs: list)the difference between each item and the one before

sum

sum(xs: list): add up a list; works with units

[1, 2, 3].sum
# → 6
[1 m, 50 cm].sum
# → 1.5 m

See also: avg, reduce

avg

avg(xs: list): mean of a list

[1, 2, 4].avg
# → 2.33333
[2 h, 30 min].avg
# → 1.25 h

See also: sum, median

product

product(xs: list): multiply a list together

[2, 3, 4].product
# → 24
[2 m, 3 m].product
# → 6 m^2

See also: sum, factorial

median

median(xs: list): middle value; mean of the middle two for an even count

[3, 1, 2].median
# → 2
[1 m, 3 m, 50 cm, 2 m].median
# → 1.5 m

See also: avg, percentile

mode

mode(xs: list): most common item; the first one on ties

[1, 2, 2, 3].mode
# → 2
"hello".chars.mode
# → "l"

See also: median, count

percentile

percentile(xs: list, p: num): the p-th percentile (0-100), interpolating between items

[1, 2, 3, 4, 5].percentile(90)
# → 4.6
(1..=100).percentile(25)
# → 25.75

See also: median

variance

variance(xs: list): sample variance (n - 1)

[2, 4, 4, 4, 5, 5, 7, 9].variance
# → 4.57143

See also: stdev

stdev

stdev(xs: list): sample standard deviation (n - 1); works with units

[2, 4, 4, 4, 5, 5, 7, 9].stdev
# → 2.13809
[1 m, 2 m, 3 m].stdev
# → 1 m

See also: variance, avg

min

min(xs: list) / min(a: any, b: any, ...): smallest value

min(3, 9, 4)
# → 3
[2 km, 1 mi].min
# → 1 mi

See also: max, sort

max

max(xs: list) / max(a: any, b: any, ...): largest value

max(3, 9, 4)
# → 9
["b", "a"].max
# → "b"

See also: min, sort

describe

describe(xs: list): n, mean, stdev, min, quartiles and max in one map; units welcome

[3, 1, 4, 1, 5, 9, 2, 6].describe
# → {n: 8, mean: 3.875, stdev: 2.74838, min: 1, p25: 1.75, median: 3.5, p75: 5.25, max: 9}

See also: avg, percentile

corr

corr(xs: list, ys: list): Pearson correlation, from -1 to 1

corr([1, 2, 3, 4], [2, 4, 5, 9])
# → 0.964764

See also: fit

fit

fit(xs: list, ys: list): least-squares line: {slope, intercept, r2}

fit([1, 2, 3], [2, 4, 6])
# → {slope: 2, intercept: 0, r2: 1}
fit([1, 2, 3, 4], [2.1, 3.9, 6.2, 7.8])
# → {slope: 1.94, intercept: 0.15, r2: 0.995661}

See also: corr

zscore

zscore(xs: list): how many standard deviations each item is from the mean

[2, 4, 4, 4, 5, 5, 7, 9].zscore
# → [-1.40312, -0.467707, -0.467707, -0.467707, 0, 0, 0.935414, 1.87083]

See also: normalize, stdev

normalize

normalize(xs: list): rescale so the min is 0 and the max is 1

[10, 15, 20].normalize
# → [0, 0.5, 1]

See also: zscore, lerp

cumsum

cumsum(xs: list): running totals

[1, 2, 3, 4].cumsum
# → [1, 3, 6, 10]
[1 km, 500 m].cumsum
# → [1 km, 1.5 km]

See also: deltas, sum

deltas

deltas(xs: list): the difference between each item and the one before

[1, 4, 9, 16].deltas
# → [3, 5, 7]
[1, 4, 9, 16].deltas.deltas
# → [2, 2]

See also: cumsum, windows

More examples

stats

# standard deviation
[2, 4, 4, 4, 5, 5, 7, 9].stdev
# → 2.13809
# percentile
(1..=100).percentile(90)
# → 90.1
# summary
[3, 1, 4, 1, 5, 9, 2, 6].describe
# → {n: 8, mean: 3.875, stdev: 2.74838, min: 1, p25: 1.75, median: 3.5, p75: 5.25, max: 9}
# trend line
fit([1, 2, 3, 4], [2.1, 3.9, 6.2, 7.8])
# → {slope: 1.94, intercept: 0.15, r2: 0.995661}
# running total
[$5, $12, $3].cumsum
# → [$5.00, $17.00, $20.00]

math.trig

sine, cosine and friends; angles as units

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
sin(x: num|quantity)sine of radians or an angle unit
cos(x: num|quantity)cosine of radians or an angle unit
tan(x: num|quantity)tangent of radians or an angle unit
asin(x: num)arcsine, as an angle
acos(x: num)arccosine, as an angle
atan(x: num)arctangent, as an angle
atan2(y: num|quantity, x: num|quantity)angle of the point (x, y), as an angle
sinh(x: num)hyperbolic sine
cosh(x: num)hyperbolic cosine
tanh(x: num)hyperbolic tangent

sin

sin(x: num|quantity): sine of radians or an angle unit

sin(30 deg)
# → 0.5
sin(pi / 2)
# → 1

See also: cos, tan, asin

cos

cos(x: num|quantity): cosine of radians or an angle unit

cos(60 deg)
# → 0.5

See also: sin, tan, acos

tan

tan(x: num|quantity): tangent of radians or an angle unit

tan(45 deg)
# → 1

See also: sin, cos, atan

asin

asin(x: num): arcsine, as an angle

asin(1) to deg
# → 90 deg

See also: sin

acos

acos(x: num): arccosine, as an angle

acos(0) to deg
# → 90 deg

See also: cos

atan

atan(x: num): arctangent, as an angle

atan(1) to deg
# → 45 deg

See also: tan, atan2

atan2

atan2(y: num|quantity, x: num|quantity): angle of the point (x, y), as an angle

atan2(1, -1) to deg
# → 135 deg

See also: atan, hypot

sinh

sinh(x: num): hyperbolic sine

sinh(1)
# → 1.1752

See also: cosh, tanh

cosh

cosh(x: num): hyperbolic cosine

cosh(1)
# → 1.54308

See also: sinh, tanh

tanh

tanh(x: num): hyperbolic tangent

tanh(1)
# → 0.761594

See also: sinh, cosh

More examples

trig

# trig takes angle units
sin(30 deg)
# → 0.5
# radians back to degrees
atan2(1, 1) to deg
# → 45 deg

math.numtheory

gcd, primes, factoring, factorials, modular powers; exact at any size

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
gcd(a: int, b: int, ...) / gcd(xs: list)greatest common divisor
lcm(a: int, b: int, ...) / lcm(xs: list)least common multiple
is_prime(n: int)primality (Miller-Rabin; exact below 3e24)
factors(n: int)prime factors, smallest first
factorial(n: int)n!, exact
choose(n: int, k: int)ways to pick k of n, exact
mod_pow(b: int, e: int, m: int)b ** e % m without the huge power
fib(n: int)the nth Fibonacci number (fib(0) = 0), exact
next_prime(n: int)the smallest prime greater than n
totient(n: int)Euler’s φ: how many of 1..n share no factor with n
isqrt(n: int)the integer square root, floor(√n), exact at any size
cfrac(x: num, terms?: int)continued fraction terms [a0; a1, a2, …]; exact for fractions, up to terms (default 20) for floats

gcd

gcd(a: int, b: int, ...) / gcd(xs: list): greatest common divisor

gcd(12, 18)
# → 6
[12, 18, 27].gcd
# → 3

See also: lcm

lcm

lcm(a: int, b: int, ...) / lcm(xs: list): least common multiple

lcm(4, 6)
# → 12
(1..=20).lcm
# → 232792560

See also: gcd

is_prime

is_prime(n: int): primality (Miller-Rabin; exact below 3e24)

is_prime(97)
# → true
is_prime(2 ** 61 - 1)
# → true

See also: factors

factors

factors(n: int): prime factors, smallest first

360.factors
# → [2, 2, 2, 3, 3, 5]
factors(2 ** 32 + 1)
# → [641, 6700417]

See also: is_prime, gcd

factorial

factorial(n: int): n!, exact

factorial(5)
# → 120
factorial(30)
# → 265252859812191058636308480000000

See also: choose

choose

choose(n: int, k: int): ways to pick k of n, exact

choose(5, 2)
# → 10
choose(52, 5)
# → 2598960

See also: factorial

mod_pow

mod_pow(b: int, e: int, m: int): b ** e % m without the huge power

mod_pow(2, 100, 7)
# → 2
mod_pow(3, 10 ** 18, 1000000007)
# → 246336683

See also: gcd

fib

fib(n: int): the nth Fibonacci number (fib(0) = 0), exact

fib(10)
# → 55
fib(100)
# → 354224848179261915075

See also: factorial

next_prime

next_prime(n: int): the smallest prime greater than n

next_prime(100)
# → 101
next_prime(2 ** 64)
# → 18446744073709551629

See also: is_prime

totient

totient(n: int): Euler’s φ: how many of 1..n share no factor with n

totient(36)
# → 12
totient(97)
# → 96

See also: factors, gcd

isqrt

isqrt(n: int): the integer square root, floor(√n), exact at any size

isqrt(99)
# → 9
isqrt(10 ** 40)
# → 100000000000000000000

See also: sqrt

cfrac

cfrac(x: num, terms?: int): continued fraction terms [a0; a1, a2, …]; exact for fractions, up to terms (default 20) for floats

(415/93).cfrac
# → [4, 2, 6, 7]
pi.cfrac(5)
# → [3, 7, 15, 1, 292]
sqrt(2).cfrac(6)
# → [1, 2, 2, 2, 2, 2]

See also: frac

More examples

numtheory

# prime factors
factors(2 ** 32 + 1)
# → [641, 6700417]
# is a Mersenne number prime
is_prime(2 ** 61 - 1)
# → true
# poker hands
choose(52, 5)
# → 2598960
# modular power
mod_pow(3, 1000, 7)
# → 4
# a 209-digit Fibonacci
fib(1000).str.len
# → 209
# π's famous fractions
pi.cfrac(4)
# → [3, 7, 15, 1]
# the next prime after a googol
(10 ** 100).next_prime - 10 ** 100
# → 267

math.bits

count, test, set, rotate and swap bits

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

conversions

# to bits
0xf0 to bits
# → "0b1111_0000"

Functions

functiondescription
popcount(n: int)number of 1 bits (two’s complement for negatives)
bit(n: int, i: int)bit i of n (0 is the lowest), as 0 or 1
set_bit(n: int, i: int)n with bit i set
clear_bit(n: int, i: int)n with bit i cleared
rotl(n: int, k: int, width: int)rotate the low width bits left by k
rotr(n: int, k: int, width: int)rotate the low width bits right by k
byteswap(n: int, width: int)reverse the bytes of a width-bit number

popcount

popcount(n: int): number of 1 bits (two’s complement for negatives)

popcount(0b1011)
# → 3
popcount(-1)
# → 64

See also: bit

bit

bit(n: int, i: int): bit i of n (0 is the lowest), as 0 or 1

bit(0b100, 2)
# → 1

See also: set_bit, clear_bit, popcount

set_bit

set_bit(n: int, i: int): n with bit i set

set_bit(0b1, 4)
# → 0b10001

See also: clear_bit, bit

clear_bit

clear_bit(n: int, i: int): n with bit i cleared

clear_bit(0xff, 0)
# → 0xfe

See also: set_bit, bit

rotl

rotl(n: int, k: int, width: int): rotate the low width bits left by k

rotl(0x81, 1, 8)
# → 0x3
rotl(0x80000000, 1, 32)
# → 0x1

See also: rotr

rotr

rotr(n: int, k: int, width: int): rotate the low width bits right by k

rotr(0x81, 1, 8)
# → 0xc0

See also: rotl

byteswap

byteswap(n: int, width: int): reverse the bytes of a width-bit number

byteswap(0x1234, 16)
# → 0x3412
byteswap(0x12345678, 32)
# → 0x78563412

See also: rotl

More examples

bits

# see the bits
0xf0 to bits
# → "0b1111_0000"
# xor
0b1010 ^ 0b0110
# → 0b1100
# count set bits
popcount(0xff)
# → 8
# rotate within 8 bits
rotr(1, 1, 8)
# → 128
# swap byte order
byteswap(0x1234, 16)
# → 0x3412
# clear a bit
clear_bit(0xff, 0)
# → 0xfe

math.bases

hex, binary, octal and any base 2-36; digits of a number

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

conversions

# to hex bin oct dec
255 to bin
# → 0b11111111
# to hex(bits), to base(b)
-1 to hex(16)
# → 0xffff

Functions

functiondescription
hex(v: int|float|str, bits?: int)same as v to hex / v to hex(bits); strings become hex bytes
bin(v: int|float, bits?: int)same as v to bin / v to bin(bits)
oct(v: int|float, bits?: int)same as v to oct
dec(v: int|float, bits?: int)same as v to dec; with bits, reads two’s complement as unsigned
base(v: int|float, b: int)same as v to base(b), any base 2-36
digits(n: int)list of digits in the number’s own base
from_digits(xs: list, base?: int)build a number from digits, kept in that base

hex

hex(v: int|float|str, bits?: int): same as v to hex / v to hex(bits); strings become hex bytes

hex(255)
# → 0xff
hex(-1, 16)
# → 0xffff
hex("hi")
# → "6869"

See also: bin, base, int

bin

bin(v: int|float, bits?: int): same as v to bin / v to bin(bits)

bin(10)
# → 0b1010
bin(5, 8)
# → 0b00000101

See also: hex, oct

oct

oct(v: int|float, bits?: int): same as v to oct

oct(8)
# → 0o10

See also: hex, bin

dec

dec(v: int|float, bits?: int): same as v to dec; with bits, reads two’s complement as unsigned

dec(0xff)
# → 255
dec(-1, 8)
# → 255

See also: hex, int

base

base(v: int|float, b: int): same as v to base(b), any base 2-36

base(35, 36)
# → 36#z
base(10, 3)
# → 3#101

See also: hex, digits

digits

digits(n: int): list of digits in the number’s own base

1234.digits
# → [1, 2, 3, 4]
0b1011.digits
# → [1, 0, 1, 1]

See also: from_digits

from_digits

from_digits(xs: list, base?: int): build a number from digits, kept in that base

[1, 2, 3].from_digits
# → 123
[1, 0, 1, 1].from_digits(2)
# → 0b1011

See also: digits

More examples

bases

# number bases
255 to bin
# → 0b11111111
# any base back to decimal
36#zz to dec
# → 1295
# hex of a big int
factorial(20) to hex
# → 0x21c3677c82b40000
# results keep their base
0xff + 1
# → 0x100
# two's complement
-1 to hex(16)
# → 0xffff
# base 36
255 to base(36)
# → 36#73
# digit sum
(2 ** 100).digits.sum
# → 115

math.formatting

printf and format specs, commas, numbers in words, ordinals, Roman numerals

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

format specs

# "{x:.2f}"  fixed decimals
"{pi:.2f}"
# → "3.14"
# "{x:>8}"  width, align < > ^
"[{42:>8}]"
# → "[      42]"
# "{n:08x}"  zero pad; x X b o d
"{255:08x}"
# → "000000ff"
# "{n:#06x}"  # adds 0x 0b 0o
"{255:#06x}"
# → "0x00ff"
# "{x:,}"  commas; also + e %
"{1234567:,}"
# → "1,234,567"
# format("%5.2f", x)  printf
format("%5.2f|%-4d|", pi, 7)
# → " 3.14|7   |"

conversions

# to words
1999 to words
# → "one thousand nine hundred ninety-nine"
# to roman
1999 to roman
# → "MCMXCIX"

Functions

functiondescription
fixed(x: num|quantity, digits: int)string with exactly that many decimals; keeps units
sci(x: num|quantity, digits?: int)string in scientific notation
percent(x: num|quantity, digits?: int)string as a percentage
commas(x: num|quantity, digits?: int)string with thousands separators
spell(n: int)an integer in English words; same as n to words
ordinal(n: int)1st, 2nd, 3rd, 4th, …, 11th, 12th, 13th, 21st
human_bytes(n: num|quantity)a byte count (or data quantity) in binary units (KiB = 1024 B), one decimal
roman(v: int|str)an int 1-3999 as a Roman numeral, or a numeral back to an int; same as n to roman

fixed

fixed(x: num|quantity, digits: int): string with exactly that many decimals; keeps units

pi.fixed(2)
# → "3.14"
(5 km to mi).fixed(1)
# → "3.1 mi"

See also: sci, commas, round

sci

sci(x: num|quantity, digits?: int): string in scientific notation

123456.sci
# → "1.23456e5"
123456.sci(2)
# → "1.23e5"

See also: fixed

percent

percent(x: num|quantity, digits?: int): string as a percentage

0.256.percent
# → "25.6%"
(1/3).percent(1)
# → "33.3%"

See also: fixed

commas

commas(x: num|quantity, digits?: int): string with thousands separators

1234567.commas
# → "1,234,567"
1234.5.commas(2)
# → "1,234.50"

See also: fixed

spell

spell(n: int): an integer in English words; same as n to words

spell(42)
# → "forty-two"
1999 to words
# → "one thousand nine hundred ninety-nine"
-1000001 to words
# → "minus one million one"

See also: ordinal, roman

ordinal

ordinal(n: int): 1st, 2nd, 3rd, 4th, …, 11th, 12th, 13th, 21st

ordinal(3)
# → "3rd"
(1..=4).map(ordinal)
# → ["1st", "2nd", "3rd", "4th"]
ordinal(112)
# → "112th"

See also: spell

human_bytes

human_bytes(n: num|quantity): a byte count (or data quantity) in binary units (KiB = 1024 B), one decimal

123456789.human_bytes
# → "117.7 MiB"
1023.human_bytes
# → "1023 B"
3 MB.human_bytes
# → "2.9 MiB"

See also: fixed

roman

roman(v: int|str): an int 1-3999 as a Roman numeral, or a numeral back to an int; same as n to roman

roman(2026)
# → "MMXXVI"
"MCMXCIX".roman
# → 1999

More examples

formatting

# format specs
"{1234567.891:,.2f}"
# → "1,234,567.89"
# hex with a prefix
"{255:#06x}"
# → "0x00ff"
# 32-bit binary
"{0xdeadbeef:032b}"
# → "11011110101011011011111011101111"
# this year
2026 to roman
# → "MMXXVI"
# Super Bowl math
roman("LX") + 1 to roman
# → "LXI"

math.random

random numbers and picks, dice, odds in plain words, fortunes, a magic 8-ball

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
rand() / rand(a: num, b: num)float in [0, 1), or a number from a to b inclusive
choice(xs: list)random item
shuffle(xs: list)shuffled copy
roll(dice?: str)roll dice like “3d6+2”, “d20”, “4d6kh3” (keep highest 3) or “2d20kl1”; returns {total, rolls}. Default 1d6
birthday_paradox(people: int, days?: int)the chance at least two of n people share a birthday (or any of days equally likely values)
odds(p: num)a probability as “1 in N”, next to a familiar event about as likely
fortune()a fortune cookie
eight_ball(question?: str)a magic 8-ball answer; the same question gets the same answer all day
coin(n?: int)heads or tails, or a list of n flips
yes_or_no(question?: str)yes or no; leans yes on Fridays
excuse()why it doesn’t work

rand

rand() / rand(a: num, b: num): float in [0, 1), or a number from a to b inclusive

rand()
# → 0.141038
rand(1, 6)
# → 5

See also: choice, shuffle

choice

choice(xs: list): random item

["rock", "paper", "scissors"].choice
# → "scissors"

See also: rand, shuffle

shuffle

shuffle(xs: list): shuffled copy

(1..6).shuffle
# → [2, 1, 3, 4, 5]

See also: choice

roll

roll(dice?: str): roll dice like “3d6+2”, “d20”, “4d6kh3” (keep highest 3) or “2d20kl1”; returns {total, rolls}. Default 1d6

roll("3d6+2")
# → {total: 11, rolls: [2, 1, 6]}
roll("d%").total
# → 97

See also: rand, coin

birthday_paradox

birthday_paradox(people: int, days?: int): the chance at least two of n people share a birthday (or any of days equally likely values)

birthday_paradox(23)
# → 0.507297
birthday_paradox(70)
# → 0.99916
birthday_paradox(10, 100)
# → 0.371843

See also: odds, choose

odds

odds(p: num): a probability as “1 in N”, next to a familiar event about as likely

odds(1 / 1000)
# → "1 in 1,000: about as likely as flipping ten heads in a row (1 in 1,024)"
odds(0.5)
# → "1 in 2: about as likely as a coin landing heads (1 in 2)"
odds(2 ** -64)
# → "1 in 1.84 × 10^19: about as likely as a perfect March Madness bracket (1 in 9.20 × 10^18)"

See also: birthday_paradox

fortune

fortune(): a fortune cookie

fortune()
# → "Do not trust the last slice. It was left for a reason."

See also: eight_ball

eight_ball

eight_ball(question?: str): a magic 8-ball answer; the same question gets the same answer all day

eight_ball("is it Friday?")
# → "Most likely."

See also: yes_or_no, fortune

coin

coin(n?: int): heads or tails, or a list of n flips

coin()
# → "heads"
coin(3)
# → ["heads", "heads", "heads"]

See also: rand

yes_or_no

yes_or_no(question?: str): yes or no; leans yes on Fridays

yes_or_no()
# → "no"

See also: eight_ball

excuse

excuse(): why it doesn’t work

excuse()
# → "The bus left early. I saw it. It saw me."

See also: fortune

More examples

random

# roll a die
rand(1, 6)
# → 1
# float in a range
rand(1.0, 2.0)
# → 1.19859
# pick one
["rock", "paper", "scissors"].choice
# → "scissors"
# shuffle
(1..=5).shuffle
# → [2, 5, 1, 4, 3]
# a D&D stat: 4d6, keep the best 3
roll("4d6kh3")
# → {total: 11, rolls: [2, 1, 5, 4]}
# attack with advantage
roll("2d20kh1+5").total
# → 19
# best of five
coin(5)
# → ["heads", "heads", "heads", "tails", "tails"]
# a party of 23
birthday_paradox(23)
# → 0.507297
# how unlikely is the jackpot?
odds(1 / 292201338)
# → "1 in 292,201,338: about as likely as winning the Powerball jackpot (1 in 292,201,338)"
# ask the ball
eight_ball("should I get a dog?")
# → "Concentrate and ask again."
# why you're late
excuse()
# → "I was on hold for three hours."

math.uncertainty

measurements with an error, 5 ± 0.1 m, carried through arithmetic

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

operators

# x ± e (or x +- e)
5 ± 0.1 m
# x ± n%
20 ± 5%
# → 20 ± 1

pretty

# long: value's best prefix for both, separators
pretty(1500 ± 20 m)
# → "1.5 ± 0.02 km"
# short: same, K M B T
pretty(1234567 ± 1000, "short")
# → "1.23M ± 1K"

propagation

# errors add in quadrature
(5 ± 0.3) + (2 ± 0.4)
# → 7 ± 0.5
# relative errors too
(10 ± 1 m) * (2 ± 0.1 m)
# → 20 ± 2.23607 m^2
# through functions
sqrt(16 ± 1)
# → 4 ± 0.125
# and conversions
5 ± 0.1 km to mi
# → 3.10686 ± 0.0621371 mi

Functions

functiondescription
value(x: uncertain)the central value, dropping the uncertainty
error(x: uncertain)the uncertainty, in the same unit

value

value(x: uncertain): the central value, dropping the uncertainty

(5 ± 0.1 m).value
# → 5 m

See also: error

error

error(x: uncertain): the uncertainty, in the same unit

(5 ± 0.1 m).error
# → 0.1 m
(20 ± 5%).error
# → 1

See also: value

More examples

uncertainty

# area of a measured rectangle
(2 ± 0.05 m) * (3 ± 0.05 m)
# → 6 ± 0.180278 m^2
# pendulum period from its length in m
2 * pi * sqrt((1 ± 0.01) / 9.81)
# → 2.00607 ± 0.0100303

math.complex

complex numbers, 2 + 3i; a number touching i is imaginary

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

literals

# ni
3i
# a + bi
2 + 3i

operators

# + - * / **
(1 + 2i) * (3 - 1i)
# → 5 + 5i

also takes complex

# abs exp ln sqrt
sqrt(-4 + 0i)
# → 2i

Functions

functiondescription
re(z: num|complex)real part
im(z: num|complex)imaginary part
conj(z: num|complex)complex conjugate
arg(z: num|complex)angle from the positive real axis, as an angle
csqrt(z: num|complex)square root that goes complex for negatives (principal root)
polar(r: num, theta: num|quantity) / polar(z: num|complex)a complex number from a length and an angle; with one argument, z in polar form (to polar)

re

re(z: num|complex): real part

re(2 + 3i)
# → 2

See also: im

im

im(z: num|complex): imaginary part

im(2 + 3i)
# → 3

See also: re

conj

conj(z: num|complex): complex conjugate

conj(2 + 3i)
# → 2 - 3i

See also: re, im

arg

arg(z: num|complex): angle from the positive real axis, as an angle

arg(1i) to deg
# → 90 deg

See also: polar, abs

csqrt

csqrt(z: num|complex): square root that goes complex for negatives (principal root)

csqrt(-4)
# → 2i

See also: sqrt

polar

polar(r: num, theta: num|quantity) / polar(z: num|complex): a complex number from a length and an angle; with one argument, z in polar form (to polar)

polar(2, 90 deg)
# → 2i
polar(1i)
# → "polar(1, 90 deg)"

See also: arg, abs

More examples

complex

# Euler's identity
e ** (1i * pi)
# → -1 + 0i
# roots of x² + 2x + 5
[-1 + csqrt(-4) / 2, -1 - csqrt(-4) / 2]
# → [-1 + 1i, -1 - 1i]
# polar form
1 + 1i to polar
# → "polar(1.41421, 45 deg)"

math.linalg

dot and cross products, matrix multiply, determinant, inverse, linear systems

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
dot(a: list, b: list)dot product of two vectors of the same length
cross(a: list, b: list)cross product of two 3-vectors
norm(v: list)length of a vector
transpose(m: list)swap rows and columns
matmul(a: list, b: list)matrix product; a’s column count must match b’s row count
det(m: list)determinant of a square matrix
inv(m: list)inverse of a square matrix; exact for ints and fractions
solve(a: list, b: list)x such that a × x = b, for square a

dot

dot(a: list, b: list): dot product of two vectors of the same length

dot([1, 2, 3], [4, 5, 6])
# → 32

See also: cross, norm

cross

cross(a: list, b: list): cross product of two 3-vectors

cross([1, 0, 0], [0, 1, 0])
# → [0, 0, 1]

See also: dot

norm

norm(v: list): length of a vector

norm([3, 4])
# → 5
norm([1 m, 1 m])
# → 1.41421 m

See also: dot, hypot

transpose

transpose(m: list): swap rows and columns

[[1, 2, 3], [4, 5, 6]].transpose
# → [[1, 4], [2, 5], [3, 6]]

See also: matmul

matmul

matmul(a: list, b: list): matrix product; a’s column count must match b’s row count

matmul([[1, 2], [3, 4]], [[5], [6]])
# → [[17], [39]]

See also: transpose, inv

det

det(m: list): determinant of a square matrix

det([[1, 2], [3, 4]])
# → -2

See also: inv, solve

inv

inv(m: list): inverse of a square matrix; exact for ints and fractions

inv([[4, 7], [2, 6]])
# → [[0.6, -0.7], [-0.2, 0.4]]

See also: det, solve

solve

solve(a: list, b: list): x such that a × x = b, for square a

solve([[2, 1], [1, 3]], [3, 5])
# → [0.8, 1.4]
solve([[1, 1, 1], [0, 2, 5], [2, 5, -1]], [6, -4, 27])
# → [5, 3, -2]

See also: inv, det

More examples

linalg

# solve 2x + y = 3, x + 3y = 5
solve([[2, 1], [1, 3]], [3, 5]).map(frac)
# → [4/5, 7/5]
# exact inverse
inv([[2, 1], [1, 1]])
# → [[1, -1], [-1, 2]]
# rotate (1, 0) by 90°
matmul([[cos(90 deg), -sin(90 deg)], [sin(90 deg), cos(90 deg)]], [[1], [0]])
# → [[6.12323e-17], [1]]
# distance between points
norm([3 m, 4 m])
# → 5 m

math.calculus

numeric roots, derivatives and integrals of functions

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
root(f: fn, guess: num) / root(f: fn, lo: num, hi: num)an x where f(x) = 0: Newton’s method from a guess, or bisection between lo and hi where f changes sign
deriv(f: fn, x: num)f’(x) by central difference
integrate(f: fn, a: num|quantity, b: num|quantity)∫ f from a to b by Simpson’s rule (1000 steps); units carry through

root

root(f: fn, guess: num) / root(f: fn, lo: num, hi: num): an x where f(x) = 0: Newton’s method from a guess, or bisection between lo and hi where f changes sign

root(|x| x ** 2 - 2, 1)
# → 1.41421
root(|x| x ** 3 - x - 1, 1, 2)
# → 1.32472

See also: deriv

deriv

deriv(f: fn, x: num): f’(x) by central difference

deriv(|x| x ** 2, 3)
# → 6
deriv(sin, 0)
# → 1

See also: integrate, root

integrate

integrate(f: fn, a: num|quantity, b: num|quantity): ∫ f from a to b by Simpson’s rule (1000 steps); units carry through

integrate(|x| x ** 2, 0, 3)
# → 9
integrate(sin, 0, pi)
# → 2

See also: deriv

More examples

calculus

# √2 the hard way
root(|x| x ** 2 - 2, 1)
# → 1.41421
# where cos meets x
root(|x| cos(x) - x, 0, 1)
# → 0.739085
# slope of x³ at 2
deriv(|x| x ** 3, 2)
# → 12
# area under a bell curve
integrate(|x| exp(-x ** 2), -10, 10) ** 2
# → 3.14159
# distance from speed
integrate(|t| 9.8 m/s^2 * t, 0 s, 3 s)
# → 44.1 m

units

numbers with units, combined and converted; live currency rates

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

types

# quantity
5 km

operators

# qty ± qty
1 km + 300 m
# → 1.3 km
# qty * / qty
100 km / 2 h
# → 50 km/h
# qty ** int
(3 m) ** 2
# → 9 m^2
# qty < qty
1 mi > 1 km
# → true

pretty

# long: best prefix, thousands separators
pretty(12345678 m)
# → "12,345.7 km"
# short: best prefix, K M B T
pretty(12345678 m, "short")
# → "12.3K km"
# durations split into d h min s, money keeps its own display: see time, money

conversions

# to unit
5 km to mi
# → 3.10686 mi
# in unit
5 km in mi
# → 3.10686 mi
# to unit unit
1.8 m to ft in
# → "5 ft 10.8661 in"
# to compound
100 km / 2 h to mph
# → 31.0686 mph

your own units

# unit sprint = 2 wk        then  3 sprint to d → 42 d
# unit pizza                a new base unit, for counting
# unit slice = pizza / 8    then  3 slice to pizza → 0.375 pizza

Units

kindnames
lengthm, meter, meters, metre, metres; km, kilometer, kilometers; cm, centimeter, centimeters; mm, millimeter, millimeters; µm, um, μm, micrometer, micrometers, micron; nm, nanometer, nanometers; mi, mile, miles; yd, yard, yards; ft, foot, feet; in, inch, inches; px, pixel, pixels; nmi; au; ly, lightyear, lightyears
masskg, kilogram, kilograms; g, gram, grams; mg, milligram, milligrams; µg, ug, μg, microgram, micrograms; t, tonne, tonnes; lb, lbs, pound, pounds; oz, ounce, ounces; st, stone
times, sec, secs, second, seconds; ms, millisecond, milliseconds; µs, us, μs, microsecond, microseconds; ns, nanosecond, nanoseconds; min, mins, minute, minutes; h, hr, hrs, hour, hours; d, day, days; wk, week, weeks; mo, month, months; yr, year, years; workday, workdays; workwk, workweek, workweeks; workmo, workmonth, workmonths; workyr, workyear, workyears
temperatureK, kelvin; C, celsius, degC; F, fahrenheit, degF
volumeL, l, liter, liters, litre, litres; mL, ml, milliliter, milliliters; gal, gallon, gallons; qt, quart, quarts; pt, pint, pints; cup, cups; floz; tbsp, tablespoon, tablespoons; tsp, teaspoon, teaspoons
areaha, hectare, hectares; acre, acres
speedkph, kmh; mph; kn, knot, knots
databit, bits; B, byte, bytes; KB; MB; GB; TB; PB; KiB; MiB; GiB; TiB; PiB; kbit, Kb; Mbit, Mb; Gbit, Gb
ratebps; kbps; Mbps; Gbps
energyJ, joule, joules; kJ; MJ; cal, calorie, calories; kcal, Cal; Wh; kWh; eV; BTU, btu
powerW, watt, watts; kW; MW; hp, horsepower
pressurePa, pascal; kPa; MPa; bar; atm; psi; mmHg
forceN, newton, newtons; kN; lbf
anglerad, radian, radians; deg, degree, degrees; turn, turns
currentA, amp, amps, ampere, amperes; mA, milliamp, milliamps; µA, uA, μA; kA
voltageV, volt, volts; mV; µV, uV, μV; kV
resistanceΩ, ohm, ohms; mΩ, mohm; kΩ, kohm; MΩ, Mohm
chargecoulomb, coulombs; mAh; Ah
capacitancefarad, farads; mF; µF, uF, μF; nF; pF
conductanceS, siemens; mS
magnetic_fluxWb, weber, webers; mWb
magnetic_fieldT, tesla, teslas; mT; µT, uT, μT
inductanceH, henry, henries; mH; µH, uH, μH
frequencyHz, hertz; kHz; MHz; GHz; rpm
amountmol, mole, moles; mmol; µmol, umol, μmol; kmol
concentrationM, molar; mM, millimolar; µM, uM, μM, micromolar
lightlm, lumen, lumens; cd, candela, candelas
illuminancelx, lux; fc, footcandle, footcandles
doseSv, sievert, sieverts; mSv, millisievert, millisieverts; µSv, uSv, μSv, microsievert, microsieverts

Submodules

moduleabout
constantsphysical constants (CODATA 2018); Planck’s constant is h_planck
moneycurrencies, live exchange rates, paid time like workday and workyr
goofybananas for scale, smoots, fortnights; every item also works as item_for_scale
kitchencups to grams with ingredient densities

Functions

functiondescription
simplify(q: quantity)the quantity in the prefixed unit of the same family that reads best, like 1.2 m or 3.6 kW; also to best
all_units()every unit as a table: {name, full, desc, aliases, kind, si, source}; source is units, goofy, currency or user
db(ratio: num, kind?: str)a power ratio in decibels (10 log10); kind “amplitude” for voltage or pressure ratios (20 log10)
from_db(db: num, kind?: str)decibels back to a power ratio, or an amplitude ratio with “amplitude”

simplify

simplify(q: quantity): the quantity in the prefixed unit of the same family that reads best, like 1.2 m or 3.6 kW; also to best

0.0012 km to best
# → 1.2 m
3600 J/s to best
# → 3.6 kW
1 kg*m^2/s^2 to best
# → 1 J
1.5e9 B.simplify
# → 1.5 GB

all_units

all_units(): every unit as a table: {name, full, desc, aliases, kind, si, source}; source is units, goofy, currency or user

all_units().len
# → 266
all_units().filter(|u| u.kind == "length").map(|u| u.name).take(5)
# → ["m", "km", "cm", "mm", "µm"]
all_units().filter(|u| u.source == "goofy")[0]
# → {name: "banana", full: "banana", desc: "a typical banana, 17.8 cm; the internet's favorite scale", aliases: ["bananas"], kind: "length", si: 0.178 m, source: "goofy"}

See also: simplify

db

db(ratio: num, kind?: str): a power ratio in decibels (10 log10); kind “amplitude” for voltage or pressure ratios (20 log10)

db(2)
# → 3.0103
db(1000)
# → 30
db(2, "amplitude")
# → 6.0206

See also: from_db, log

from_db

from_db(db: num, kind?: str): decibels back to a power ratio, or an amplitude ratio with “amplitude”

from_db(3)
# → 1.99526
from_db(-6, "amplitude")
# → 0.501187

See also: db

More examples

units

# speed from distance and time
100 km / 2 h to mph
# → 31.0686 mph
# feet and inches
1.8 m to ft in
# → "5 ft 10.8661 in"
# mixed units add
5 ft 11 in to cm
# → 180.34 cm
# drive time
300 mi / 65 mph to h min
# → "4 h 36.9231 min"
# download time
4 GB / 100 Mbps to min
# → 5.33333 min
# why a 1 TB disk shows less
1 TB to GiB
# → 931.323 GiB
# area
3 m * 4 m to ft^2
# → 129.167 ft^2
# running pace
1 h / 10 km to min/mi
# → 9.65606 min/mi
# force
9.81 m/s^2 * 70 kg to N
# → 686.7 N
# a month of a 60 W bulb
60 W * 8 h * 30 to kWh
# → 14.4 kWh
# temperature
101 F to C
# → 38.3333 C
# a 1080p screen at 96 dpi
1080 px to in
# → 11.25 in
# twice the power
db(2)
# → 3.0103
# +3 dB louder means
from_db(3)
# → 1.99526
# 20 USD to EUR   (currencies use live rates)

units.constants

physical constants (CODATA 2018); Planck’s constant is h_planck

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

constants

# speed of light
c
# → 299792458 m/s
# gravitational constant
G
# → 6.6743e-11 m^3/kg/s^2
# Planck constant
h_planck
# → 6.62607e-34 J*s
# reduced Planck constant
hbar
# → 1.05457e-34 J*s
# Boltzmann constant
k_B
# → 1.38065e-23 J/K
# Avogadro constant
N_A
# → 6.02214e23 1/mol
# gas constant
R
# → 8.31446 J/mol/K
# elementary charge
e_charge
# → 1.60218e-19 coulomb
# electron mass
m_e
# → 9.10938e-31 kg
# proton mass
m_p
# → 1.67262e-27 kg
# standard gravity
g0
# → 9.80665 m/s^2
# vacuum permittivity
eps0
# → 8.85419e-12 farad/m
# vacuum permeability
mu0
# → 0.00000125664 N/A^2
# Stefan-Boltzmann constant
sigma
# → 5.67037e-8 W/m^2/K^4

More examples

constants

# mass-energy
1 kg * c**2 to J
# → 8.98755e16 J
# light from the sun
1 au / c to min
# → 8.31675 min
# photon energy of green light
h_planck * c / 530 nm to eV
# → 2.33932 eV
# weight
70 kg * g0
# → 686.465 N

units.money

currencies, live exchange rates, paid time like workday and workyr

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

types

# money
1234.5 USD
# → $1,234.50

pretty

# long and short: as money always shows
pretty(1234567 USD, "short")
# → "$1,234,567.00"

literals

# $ € £ before a number
$25/h
# → $25.00/h
# any currency code
1200 JPY
# → ¥1,200
# workday 8 h, workwk 40 h, workmo, workyr 2080 h; USD/yr stays calendar time
# hourly to salary
$25/h to USD/workyr
# → $52,000.00/workyr
# salary to hourly
85000 USD/workyr to USD/h
# → $40.87/h

currencies

# rates load (from cache, or online after allow_network_access()) only to convert between currencies: 20 USD to EUR

More examples

money

# yearly salary from hourly
$25/h to USD/workyr
# → $52,000.00/workyr
# what a meeting costs
6 * 95000 USD/workyr * 1 h
# → $274.04
# hours of work to buy it
$1200 / ($40/h) to workday
# → 3.75 workday
# unit price
$4.99 / 12 oz to USD/lb
# → $6.65/lb
# a subscription per year
$15.99/mo to USD/yr
# → $191.88/yr

units.goofy

bananas for scale, smoots, fortnights; every item also works as item_for_scale

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Units

kindnames
lengthbanana, bananas; credit_card, credit_cards; attoparsec, attoparsecs; beard_second, beard_seconds; human_hair, human_hairs; sheet_of_paper, sheets_of_paper; light_nanosecond, light_nanoseconds; smoot, smoots; altuve, altuves; cubit, cubits; hand, hands; step, steps; giraffe, giraffes; school_bus, school_buses; blue_whale, blue_whales; football_field, football_fields; city_block, city_blocks; statue_of_liberty; furlong, furlongs; eiffel_tower, eiffel_towers; sheppey, sheppeys; league, leagues; everest, everests; marathon, marathons
massgrain_of_rice, grains_of_rice; paperclip, paperclips; us_penny, us_pennies; bowling_ball, bowling_balls; corgi, corgis; slug, slugs; firkin, firkins; grand_piano, grand_pianos; honda_civic, honda_civics; elephant, elephants; boeing_747, boeing_747s, jumbo_jet
timeplanck_time, planck_times; shake, shakes; jiffy, jiffies; blink, blinks; nanocentury, nanocenturies; moment, moments; sol, sols; microcentury, microcenturies; scaramucci, scaramuccis; fortnight, fortnights; dog_year, dog_years; friedman, friedmans
volumeshot, shots; can, cans; wine_bottle, wine_bottles; barrel, barrels, bbl; hogshead, hogsheads; bathtub, bathtubs; olympic_pool, olympic_pools
areabarn, barns; parking_space, parking_spaces; tennis_court, tennis_courts; rhode_island; wales
speedsnail, snails; walking_pace; bike_pace; mach; lightspeed
datanibble, nibbles; tweet, tweets; paragraph, paragraphs; novel, novels; floppy, floppies; photo, photos; cdrom, cdroms; human_genome, human_genomes; dvd, dvds; hour_of_hd_video, hours_of_hd_video; library_of_congress
energyaa_battery, aa_batteries; banana_cal; big_mac, big_macs; gallon_gas, gallons_gas; tnt, ton_tnt; hiroshima, hiroshimas
powerhuman_resting; donkeypower; toaster, toasters
frequencyheartbeat, heartbeats; hummingbird_wingbeat, hummingbird_wingbeats
dosebed, banana_dose

Functions

functiondescription
for_scale(q: quantity)the quantity in whichever goofy unit gives the most relatable count

for_scale

for_scale(q: quantity): the quantity in whichever goofy unit gives the most relatable count

1.8 m.for_scale
# → 3.93701 cubit
70 kg.for_scale
# → 4.79652 slug
2 h.for_scale
# → 2.28154 microcentury

More examples

goofy

# height in bananas
1.8 m to banana_for_scale
# → 10.1124 banana
# the classic speed unit
1 mph to furlong/fortnight
# → 2688 furlong/fortnight
# Harvard Bridge
364.4 smoot to m
# → 620.136 m
# pick a fitting item
8848 m.for_scale
# → 1.83263 league
# a lecture
50 min to microcentury
# → 0.950643 microcentury
# lunch energy
1 big_mac to kWh
# → 0.654331 kWh

units.kitchen

cups to grams with ingredient densities

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
density(ingredient: str)a typical density in g/mL, so volume * density is mass; baking ingredients are spooned and leveled

density

density(ingredient: str): a typical density in g/mL, so volume * density is mass; baking ingredients are spooned and leveled

density("honey")
# → 1.42 g/mL
2 tbsp * density("butter") to g
# → 28.3906 g

More examples

kitchen

# a cup of flour
1 cup * density("flour") to g
# → 125.392 g
# 250 g of sugar in cups
250 g / density("sugar") to cup
# → 1.24316 cup
# a stick of butter
0.5 cup * density("butter") to g
# → 113.562 g

finance

pay, interest and growth, loans, splitting bills, margins

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

arguments

# functions tell arguments apart by unit: $1000 a sum, $500/mo a payment,
# 7%/yr a rate, 10 yr a time, "monthly" how often interest compounds

Functions

functiondescription
salary(rate: quantity, opts?: map)pay per hour, day, week, month and year; opts {hours: 40 a week, weeks: 52 paid}
grow(sum: num|quantity, rate: quantity, time: quantity, payment?: quantity|str, compounding?: str)future value: a sum and/or payments growing at a rate
cagr(start: num|quantity, end: num|quantity, time: quantity)compound annual growth rate
doubling(rate: quantity, compounding?: str)time for money to double
apy(rate: quantity, compounding: str)effective yearly rate after compounding
real_rate(rate: quantity|num, inflation: quantity|num)return after inflation
payment(loan: num|quantity, rate: quantity, time: quantity, compounding?: str)monthly payment that pays off a loan
payoff(balance: num|quantity, rate: quantity, payment: quantity, compounding?: str)how long a payment takes to clear a balance, in whole payments
amortize(loan: num|quantity, rate: quantity, time: quantity, opts?: map)payment schedule: {n, payment, interest, principal, balance} per month; opts {extra: $200/mo}
share(sum: num|quantity, parts: int|list)split into parts that add up exactly, to the cent
settle(paid: map)who pays whom so everyone paid the same
change(from: num|quantity, to: num|quantity)fractional change from one value to another
margin(cost: num|quantity, price: num|quantity)profit as a fraction of the price
markup(cost: num|quantity, price: num|quantity)profit as a fraction of the cost

salary

salary(rate: quantity, opts?: map): pay per hour, day, week, month and year; opts {hours: 40 a week, weeks: 52 paid}

salary($25/h)
# → {hour: $25.00, day: $200.00, week: $1,000.00, month: $4,333.33, year: $52,000.00}
salary(85000 USD/yr, {hours: 37.5})
# → {hour: $43.59, day: $326.92, week: $1,634.62, month: $7,083.33, year: $85,000.00}

See also: grow

grow

grow(sum: num|quantity, rate: quantity, time: quantity, payment?: quantity|str, compounding?: str): future value: a sum and/or payments growing at a rate

grow($10000, 7%/yr, 30 yr)
# → $76,122.55
grow($0, 7%/yr, 30 yr, $500/mo)
# → $609,985.50
grow($1000, 5%/yr, 1 yr, "daily")
# → $1,051.27

See also: cagr, payment

cagr

cagr(start: num|quantity, end: num|quantity, time: quantity): compound annual growth rate

cagr($1000, $2500, 8 yr)
# → 12.1353%/yr

See also: grow, change

doubling

doubling(rate: quantity, compounding?: str): time for money to double

doubling(7%/yr)
# → 10.2448 yr

See also: grow

apy

apy(rate: quantity, compounding: str): effective yearly rate after compounding

apy(5%/yr, "daily")
# → 5.12675%/yr
apy(5%/yr, "monthly")
# → 5.11619%/yr

See also: grow

real_rate

real_rate(rate: quantity|num, inflation: quantity|num): return after inflation

real_rate(7%/yr, 3%/yr)
# → 3.8835%/yr

See also: grow

payment

payment(loan: num|quantity, rate: quantity, time: quantity, compounding?: str): monthly payment that pays off a loan

payment($400000, 6.5%/yr, 30 yr)
# → $2,528.27/mo
payment($25000, 7%/yr, 5 yr) * 5 yr
# → $29,701.80

See also: payoff, amortize

payoff

payoff(balance: num|quantity, rate: quantity, payment: quantity, compounding?: str): how long a payment takes to clear a balance, in whole payments

payoff($5000, 22%/yr, $200/mo)
# → 34 mo
today + payoff($5000, 22%/yr, $200/mo)
# → 2029-08-08

See also: payment

amortize

amortize(loan: num|quantity, rate: quantity, time: quantity, opts?: map): payment schedule: {n, payment, interest, principal, balance} per month; opts {extra: $200/mo}

amortize($1000, 12%/yr, 3 mo)
# → [{n: 1, payment: $340.02, interest: $10.00, principal: $330.02, balance: $669.98}, {n: 2, payment: $340.02, interest: $6.70, principal: $333.32, balance: $336.66}, {n: 3, payment: $340.02, interest: $3.37, principal: $336.66, balance: $0.00}]
amortize($400000, 6.5%/yr, 30 yr, {extra: $300/mo}).len * 1 mo to yr
# → 22.4167 yr

See also: payment

share

share(sum: num|quantity, parts: int|list): split into parts that add up exactly, to the cent

share($100, 3)
# → [$33.34, $33.33, $33.33]
share($100, [2, 1, 1])
# → [$50.00, $25.00, $25.00]

See also: settle

settle

settle(paid: map): who pays whom so everyone paid the same

settle({ana: $120, ben: $0, cy: $30})
# → ["ben pays ana $50.00", "cy pays ana $20.00"]

See also: share

change

change(from: num|quantity, to: num|quantity): fractional change from one value to another

change($80, $100)
# → 0.25
change(80, 60).percent
# → "-25%"

See also: margin, cagr

margin

margin(cost: num|quantity, price: num|quantity): profit as a fraction of the price

margin($60, $100)
# → 0.4

See also: markup

markup

markup(cost: num|quantity, price: num|quantity): profit as a fraction of the cost

markup($60, $100)
# → 0.666667

See also: margin

More examples

finance

# pay by the hour, day, week, month and year
salary($25/h)
# → {hour: $25.00, day: $200.00, week: $1,000.00, month: $4,333.33, year: $52,000.00}
# mortgage payment
payment($400000, 6.5%/yr, 30 yr)
# → $2,528.27/mo
# interest over the loan
payment($400000, 6.5%/yr, 30 yr) * 30 yr - $400000
# → $510,177.95
# debt-free date
today + payoff($5000, 22%/yr, $200/mo)
# → 2029-08-08
# saving $500 a month
grow($0, 7%/yr, 30 yr, $500/mo)
# → $609,985.50
# split a bill
share($100, 3)
# → [$33.34, $33.33, $33.33]
# who owes whom
settle({ana: $120, ben: $0, cy: $30})
# → ["ben pays ana $50.00", "cy pays ana $20.00"]

time

dates, durations, date math, relative text

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

types

# date
date("2026-12-25 18:30")
# → 2026-12-25 18:30:00 -06:00
# time of day: today at that time
9:30 + 45 min
# → 2026-10-08 10:15:00 -05:00
# duration
1h30m
# → 90 min

names

# now today tomorrow yesterday

pretty

# long date: time always shown
pretty(date("2026-12-25"))
# → "Friday, December 25, 2026 at 12:00 AM"
# short date
pretty(date("2026-12-25 18:30"), "short")
# → "Dec 25, 2026, 6:30 PM"
# other zones add theirs
pretty(date("2026-12-25 18:30") to "Asia/Tokyo")
# → "Saturday, December 26, 2026 at 9:30 AM JST"
# long duration: up to 3 of d h min s
pretty(5000 s)
# → "1 h 23 min 20 s"
# short duration: up to 2
pretty(5000 s, "short")
# → "1 h 23 min"

operators

# date ± time
date("2026-01-31") + 1 mo
# → 2026-02-28
# date - date
date("2027-01-01") - date("2026-12-25")
# → 7 d
# "time" - "time"
"17:00" - "08:45"
# → 8.25 h
# date < date
yesterday < now
# → true

conversions

# to unix, to unix_ms
date("2026-12-25") to unix
# → 1798178400
# to date
"2026-12-25" to date
# → 2026-12-25

Submodules

moduleabout
calendarsperiods, weekdays, business days, week numbers, month grids, cron, zodiacs
zonesthe same moment in UTC, local time, any IANA zone or a city; world clocks
skysunrise, sunset, day length and moon phase for a place and date

Functions

functiondescription
date(s: str) / date(s: str, fmt: str) / date(y: int, m: int, d: int, h?: int, min?: int, s?: int) / date(unix: int)parse ISO, US (12/25/2026), written (Dec 25 2026) or natural language dates
year(d: date)year of a date
month(d: date)month of a date (1-12)
day(d: date)day of the month
hour(d: date)hour of a date
minute(d: date)minute of a date
second(d: date)second of a date
weekday(d: date)day name
format(d: date, fmt: str) / format(fmt: str, args?: any, ...)format a date with strftime codes, or numbers printf-style
with(d: date, fields: map)same date with fields replaced: year month day hour minute second
is_weekend(d: date)Saturday or Sunday
is_weekday(d: date)Monday through Friday
is_today(d: date)same calendar day as now
is_past(d: date)before now
is_future(d: date)after now
age(d: date)whole years since d
diff(a: date, b: date)calendar difference from a to b
relative(d: date)“in 3 days”, “2 hours ago”
parts(duration: quantity)duration in up to three of d, h, min, s; or to d h min for chosen units
unix(d: date)seconds since 1970-01-01 UTC; same as d to unix
unix_ms(d: date)milliseconds since 1970-01-01 UTC; same as d to unix_ms
date_ms(ms: int)the date from Unix milliseconds, as JavaScript and Java give them
timeit(f: fn)how long calling f takes
stopwatch()time since the last stopwatch() call (0 s the first time)

date

date(s: str) / date(s: str, fmt: str) / date(y: int, m: int, d: int, h?: int, min?: int, s?: int) / date(unix: int): parse ISO, US (12/25/2026), written (Dec 25 2026) or natural language dates

date("2026-12-25")
# → 2026-12-25
date("next friday at 5pm")
# → 2026-10-09 17:00:00 -05:00
date("3 days ago")
# → 2026-10-05 07:58:47 -05:00
date("25.12.2026", "%d.%m.%Y")
# → 2026-12-25
date(2026, 12, 25, 18, 30)
# → 2026-12-25 18:30:00 -06:00
date(0)
# → 1969-12-31 18:00:00 -06:00

See also: format, with, start_of

year

year(d: date): year of a date

now.year
# → 2026

See also: month, day

month

month(d: date): month of a date (1-12)

now.month
# → 10

See also: year, day

day

day(d: date): day of the month

now.day
# → 8

See also: month, weekday

hour

hour(d: date): hour of a date

now.hour
# → 7

See also: minute, second

minute

minute(d: date): minute of a date

now.minute
# → 58

See also: hour, second

second

second(d: date): second of a date

now.second
# → 47

See also: hour, minute

weekday

weekday(d: date): day name

date("2026-12-25").weekday
# → "Friday"

See also: day, format

format

format(d: date, fmt: str) / format(fmt: str, args?: any, ...): format a date with strftime codes, or numbers printf-style

now.format("%B %d, %Y")
# → "October 08, 2026"
now.format("%H:%M")
# → "07:58"
format("%5.2f%%", 12.345)
# → "12.35%"

See also: date, fixed

with

with(d: date, fields: map): same date with fields replaced: year month day hour minute second

today.with({day: 1})
# → 2026-10-01
now.with({hour: 9, minute: 0})
# → 2026-10-08 09:00:47 -05:00

See also: start_of, date

is_weekend

is_weekend(d: date): Saturday or Sunday

date("2026-10-10").is_weekend
# → true

See also: is_weekday, add_workdays

is_weekday

is_weekday(d: date): Monday through Friday

today.is_weekday
# → true

See also: is_weekend

is_today

is_today(d: date): same calendar day as now

now.is_today
# → true
tomorrow.is_today
# → false

See also: is_past

is_past

is_past(d: date): before now

yesterday.is_past
# → true

See also: is_future, is_today

is_future

is_future(d: date): after now

tomorrow.is_future
# → true

See also: is_past

age

age(d: date): whole years since d

date("1990-06-15").age
# → 36

See also: diff

diff

diff(a: date, b: date): calendar difference from a to b

diff(date("2025-08-03"), date("2026-10-06 04:00"))
# → "1 yr 2 mo 3 d 4 h"

See also: age, relative, parts

relative

relative(d: date): “in 3 days”, “2 hours ago”

date("2026-12-25").relative
# → "in 3 months"
(now - 3 h).relative
# → "3 hours ago"

See also: diff, parts

parts

parts(duration: quantity): duration in up to three of d, h, min, s; or to d h min for chosen units

(date("2026-12-25") - date("2026-10-06 14:24")).parts
# → "79 d 10 h 36 min"
5000 s.parts
# → "1 h 23 min 20 s"

See also: relative, diff

unix

unix(d: date): seconds since 1970-01-01 UTC; same as d to unix

date(0).unix
# → 0
date(86400) to unix
# → 86400

See also: date, unix_ms

unix_ms

unix_ms(d: date): milliseconds since 1970-01-01 UTC; same as d to unix_ms

date(1).unix_ms
# → 1000

See also: date_ms, unix

date_ms

date_ms(ms: int): the date from Unix milliseconds, as JavaScript and Java give them

date_ms(1798178400000) == date(1798178400)
# → true

See also: unix_ms, date

timeit

timeit(f: fn): how long calling f takes

timeit(|| (1..1000).sum) < 1 s
# → true

See also: stopwatch

stopwatch

stopwatch(): time since the last stopwatch() call (0 s the first time)

stopwatch() >= 0 s
# → true

See also: timeit

More examples

time

# day of the week
date("2026-12-25").weekday
# → "Friday"
# month math clamps to the end
date("2026-01-31") + 1 mo
# → 2026-02-28
# plain-English dates
date("next friday")
# → 2026-10-09
# countdown
(date("2027-01-01") - now).parts
# → "84 d 17 h 1 min"
# relative time
(now - 3 h).relative
# → "3 hours ago"
# first of next month
(today + 1 mo).with({day: 1})
# → 2026-11-01
# months with a Friday the 13th
(1..=12).filter(|m| date(2026, m, 13).weekday == "Friday")
# → [2, 3, 11]
# from a Unix timestamp
date(1798178400)
# → 2026-12-25
# compact durations
1h30m + 2d4h
# → 3210 min
# hours worked
17:00 - 8:45
# → 8.25 h

time.calendars

periods, weekdays, business days, week numbers, month grids, cron, zodiacs

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
weekday_num(d: date)weekday as a number, Monday = 1 … Sunday = 7
day_of_year(d: date)day of the year, 1-366
iso_week(d: date)ISO 8601 week number (weeks start Monday)
quarter(d: date)quarter of the year, 1-4
leap_year(d: date|int)whether the year is a leap year
days_in_month(d: date)number of days in the date’s month
days_in_year(d: date)365 or 366
start_of(d: date, period: str)start of the second/minute/hour/day/week/month/quarter/year
end_of(d: date, period: str)last moment of the period
next(d: date, weekday: str) / next(cron: str, n?: int)the next given weekday strictly after d; or the next run of a cron expression, or a list of the next n
prev(d: date, weekday: str)the last given weekday strictly before d
nth_weekday(d: date, n: int, weekday: str)nth weekday of d’s month; negative counts from the end
add_workdays(d: date, n: int, holidays?: list|str)move n Monday-Friday days, skipping holidays (a list of dates, or “US”/“UK”); negative goes back
workdays(a: date, b: date, holidays?: list|str)Monday-Friday days from a up to (not including) b, minus holidays
holidays(year: int, country: str)public holidays as observed (moved off weekends): “US” federal or “UK” England bank holidays
cron(expr: str)a cron expression (minute hour day month weekday, or @daily etc.) in plain English
calendar(d: date) / calendar(year: int, month: int)month grid, weeks starting Monday
zodiac(day?: date)the Western zodiac sign for a date (default today)
chinese_zodiac(year: int|date)the Chinese zodiac element and animal; by Gregorian year, so January dates before Lunar New Year come out a year late

weekday_num

weekday_num(d: date): weekday as a number, Monday = 1 … Sunday = 7

today.weekday_num
# → 4

See also: weekday

day_of_year

day_of_year(d: date): day of the year, 1-366

date("2026-12-31").day_of_year
# → 365

See also: iso_week

iso_week

iso_week(d: date): ISO 8601 week number (weeks start Monday)

date("2026-12-31").iso_week
# → 53

See also: day_of_year, quarter

quarter

quarter(d: date): quarter of the year, 1-4

today.quarter
# → 4

See also: iso_week

leap_year

leap_year(d: date|int): whether the year is a leap year

leap_year(2028)
# → true
today.leap_year
# → false

See also: days_in_year

days_in_month

days_in_month(d: date): number of days in the date’s month

date("2028-02-10").days_in_month
# → 29

See also: days_in_year

days_in_year

days_in_year(d: date): 365 or 366

today.days_in_year
# → 365

See also: leap_year

start_of

start_of(d: date, period: str): start of the second/minute/hour/day/week/month/quarter/year

now.start_of("week")
# → 2026-10-05
now.start_of("quarter")
# → 2026-10-01

See also: end_of, with

end_of

end_of(d: date, period: str): last moment of the period

now.end_of("month")
# → 2026-10-31 23:59:59 -05:00
(today.end_of("year") - now).parts
# → "84 d 17 h 1 min"

See also: start_of

next

next(d: date, weekday: str) / next(cron: str, n?: int): the next given weekday strictly after d; or the next run of a cron expression, or a list of the next n

today.next("friday")
# → 2026-10-09
now.next("mon")
# → 2026-10-12 07:58:47 -05:00
"0 9 * * mon".next
# → 2026-10-12 09:00:00 -05:00
"@monthly".next(2)
# → [2026-11-01, 2026-12-01]

See also: prev, nth_weekday, cron

prev

prev(d: date, weekday: str): the last given weekday strictly before d

today.prev("sunday")
# → 2026-10-04

See also: next

nth_weekday

nth_weekday(d: date, n: int, weekday: str): nth weekday of d’s month; negative counts from the end

date(2026, 11, 1).nth_weekday(4, "thu")
# → 2026-11-26
date(2026, 5, 1).nth_weekday(-1, "mon")
# → 2026-05-25

See also: next

add_workdays

add_workdays(d: date, n: int, holidays?: list|str): move n Monday-Friday days, skipping holidays (a list of dates, or “US”/“UK”); negative goes back

today.add_workdays(10)
# → 2026-10-22
date("2026-12-24").add_workdays(1, "US")
# → 2026-12-28
date("2026-12-24").add_workdays(1, [date("2026-12-28")])
# → 2026-12-25

See also: workdays, holidays, is_weekend

workdays

workdays(a: date, b: date, holidays?: list|str): Monday-Friday days from a up to (not including) b, minus holidays

workdays(today, date("2026-12-25"))
# → 56
workdays(date("2026-12-01"), date("2027-01-01"), "UK")
# → 21

See also: add_workdays, holidays

holidays

holidays(year: int, country: str): public holidays as observed (moved off weekends): “US” federal or “UK” England bank holidays

holidays(2026, "US")
# → [2026-01-01, 2026-01-19, 2026-02-16, 2026-05-25, 2026-06-19, 2026-07-03, 2026-09-07, 2026-10-12, 2026-11-11, 2026-11-26, 2026-12-25]
holidays(2026, "UK").len
# → 8

See also: add_workdays, workdays

cron

cron(expr: str): a cron expression (minute hour day month weekday, or @daily etc.) in plain English

cron("0 9 * * 1-5")
# → "at 09:00, on Monday through Friday"
cron("30 4 1,15 * fri")
# → "at 04:30, on days 1 and 15 of the month, or on Friday"
cron("@hourly")
# → "at minute 0 of every hour"

See also: next

calendar

calendar(d: date) / calendar(year: int, month: int): month grid, weeks starting Monday

calendar(2026, 12)
# →    December 2026    
# → Mo Tu We Th Fr Sa Su
# →     1  2  3  4  5  6
# →  7  8  9 10 11 12 13
# → 14 15 16 17 18 19 20
# → 21 22 23 24 25 26 27
# → 28 29 30 31

See also: date

zodiac

zodiac(day?: date): the Western zodiac sign for a date (default today)

zodiac(date("2000-01-01"))
# → "♑ Capricorn"

See also: chinese_zodiac

chinese_zodiac

chinese_zodiac(year: int|date): the Chinese zodiac element and animal; by Gregorian year, so January dates before Lunar New Year come out a year late

chinese_zodiac(2026)
# → "Fire Horse"
chinese_zodiac(1984)
# → "Wood Rat"

See also: zodiac

More examples

calendars

# Thanksgiving: 4th Thursday
date(2026, 11, 1).nth_weekday(4, "thu")
# → 2026-11-26
# Memorial Day: last Monday
date(2026, 5, 1).nth_weekday(-1, "mon")
# → 2026-05-25
# 3 business days later
date("2026-12-24").add_workdays(3)
# → 2026-12-29
# workdays in December
workdays(date("2026-12-01"), date("2026-12-31"))
# → 22
# skipping US holidays
date("2026-12-24").add_workdays(3, "US")
# → 2026-12-30
# ISO week number
today.iso_week
# → 41
# what does this cron line mean?
cron("*/15 9-17 * * mon-fri")
# → "every 15 minutes, during hours 9 through 17, on Monday through Friday"
# when does it run next?
"0 9 * * 1-5".next(3)
# → [2026-10-08 09:00:00 -05:00, 2026-10-09 09:00:00 -05:00, 2026-10-12 09:00:00 -05:00]
# your sign
[zodiac(date("1990-08-10")), chinese_zodiac(1990)]
# → ["♌ Leo", "Metal Horse"]

time.zones

the same moment in UTC, local time, any IANA zone or a city; world clocks

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

conversions

# to UTC, to local
date("2026-12-25 18:30") to UTC
# → 2026-12-26 00:30:00 +00:00
# to "Zone/Name"
date("2026-12-25 18:30") to "Asia/Tokyo"
# → 2026-12-26 09:30:00 +09:00
# to "City" or "Country"
date("2026-12-25 18:30") to "Sao Paulo"
# → 2026-12-25 21:30:00 -03:00
# a zone in a variable
z = "Asia/Tokyo"; date("2026-12-25 18:30") to (z)
# → 2026-12-26 09:30:00 +09:00

Functions

functiondescription
utc(d: date)the same moment in UTC; same as d to UTC (d to "Asia/Tokyo" for any zone)
local(d: date)the same moment in the system time zone; same as d to local
clock(places: list) / clock(place: str)the time now in each zone or city

utc

utc(d: date): the same moment in UTC; same as d to UTC (d to "Asia/Tokyo" for any zone)

date(0) to UTC
# → 1970-01-01 00:00:00 +00:00
date(0).utc.year
# → 1970

See also: local, date

local

local(d: date): the same moment in the system time zone; same as d to local

(date(0) to UTC).local.year
# → 1969

See also: utc

clock

clock(places: list) / clock(place: str): the time now in each zone or city

clock(["Tokyo", "London"])
# → {Tokyo: 2026-10-08 21:58:47 +09:00, London: 2026-10-08 13:58:47 +01:00}
clock("Asia/Kolkata").hour >= 0
# → true

See also: utc, local

More examples

zones

# time zones
date("2026-12-25 18:30") to "Asia/Tokyo"
# → 2026-12-26 09:30:00 +09:00
# meeting time abroad
5pm to "London"
# → 2026-10-08 23:00:00 +01:00
# world clock
clock(["Tokyo", "London", "New York"])
# → {Tokyo: 2026-10-08 21:58:47 +09:00, London: 2026-10-08 13:58:47 +01:00, New York: 2026-10-08 08:58:47 -04:00}

time.sky

sunrise, sunset, day length and moon phase for a place and date

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

coordinates

# latitude north and longitude east are positive, in degrees
# or name a place: a city or country, or a map with lat and lon
sunset("Oslo", date(2026, 6, 21))
# → 2026-06-21 15:43:43 -05:00
# times come back in the date's time zone

Functions

functiondescription
sunrise(lat: num, lon: num, day?: date) / sunrise(place: str|map, day?: date)when the sun rises there on that day (default today), in the day’s time zone; nil during polar night or midnight sun
sunset(lat: num, lon: num, day?: date) / sunset(place: str|map, day?: date)when the sun sets there on that day (default today); nil if it doesn’t
day_length(lat: num, lon: num, day?: date) / day_length(place: str|map, day?: date)time from sunrise to sunset: 0 h in polar night, 24 h under the midnight sun
moon_phase(day?: date)the moon’s phase and how much of it is lit

sunrise

sunrise(lat: num, lon: num, day?: date) / sunrise(place: str|map, day?: date): when the sun rises there on that day (default today), in the day’s time zone; nil during polar night or midnight sun

sunrise(40.71, -74.01, date(2026, 3, 20))
# → 2026-03-20 05:59:37 -05:00
sunrise("Reykjavik").hour
# → 7

See also: sunset, day_length

sunset

sunset(lat: num, lon: num, day?: date) / sunset(place: str|map, day?: date): when the sun sets there on that day (default today); nil if it doesn’t

sunset(51.5, -0.13, date(2026, 12, 21))
# → 2026-12-21 09:53:16 -06:00
sunset("Oslo", date(2026, 6, 21))
# → 2026-06-21 15:43:43 -05:00

See also: sunrise, day_length

day_length

day_length(lat: num, lon: num, day?: date) / day_length(place: str|map, day?: date): time from sunrise to sunset: 0 h in polar night, 24 h under the midnight sun

day_length(0, 0, date(2026, 3, 20)) to h min
# → "12 h 6.66405 min"
day_length(70, 25, date(2026, 6, 21))
# → 24 h

See also: sunrise, sunset

moon_phase

moon_phase(day?: date): the moon’s phase and how much of it is lit

moon_phase()
# → "🌘 waning crescent, 9% lit"
moon_phase(date(2026, 1, 3))
# → "🌕 full moon, 100% lit"

See also: sunrise

More examples

sky

# sunset in Paris on the solstice
sunset(48.86, 2.35, date("2026-06-21T12:00+02:00") to "Europe/Paris")
# → 2026-06-21 21:57:44 +02:00
# the longest day in Oslo
day_length(59.91, 10.75, date(2026, 6, 21)) to h min
# → "18 h 49.9557 min"
# no sunrise at the pole in winter
sunrise(89, 0, date(2026, 12, 21))
# → nil
# moon on the 2026 Christmas
moon_phase(date(2026, 12, 25))
# → "🌕 full moon, 99% lit"

geo

countries and cities: lookups, distance, bearing, nearest city

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

names

# countries by name or ISO code
country("JPN").name
# → "Japan"
# short names and accents optional
[city("NYC").name, city("Sao Paulo").name]
# → ["New York City", "São Paulo"]
# the biggest city wins; add ", country" or ", state" to pick another
city("Hyderabad, PK").country
# → "Pakistan"

places

# a place is a city or country name (a country means its capital), or a map with lat and lon
great_circle("Paris", {lat: 0, lon: 0})
# → 5436.93 km

currencies

# $100 to "Vietnam" converts into a place's currency (rates load as for 20 USD to EUR)

data

# GeoNames (CC-BY 4.0, geonames.org): capitals and cities over 500k people

Functions

functiondescription
country(name: str)a country by name or ISO code: {name, code, code3, capital, currency, zones, calling, population, area, continent, flag, languages, neighbours}
city(name: str)a city: {name, country, admin, population, lat, lon, zone}; the biggest of that name unless qualified like “Portland, US”
countries()every country as a table
cities()every city as a table, biggest first
great_circle(from: str|map, to: str|map)great-circle distance between places
bearing(from: str|map, to: str|map)initial compass bearing from one place to another, clockwise from north
nearest(lat: num, lon: num)the known city closest to a point

country

country(name: str): a country by name or ISO code: {name, code, code3, capital, currency, zones, calling, population, area, continent, flag, languages, neighbours}

country("Japan")
# → {name: "Japan", code: "JP", code3: "JPN", capital: "Tokyo", currency: "JPY", zones: ["Asia/Tokyo"], calling: "+81", population: 126529100, area: 377835 km^2, continent: "Asia", flag: "🇯🇵", languages: ["ja"], neighbours: []}
country("UK").currency
# → "GBP"

See also: city, countries

city

city(name: str): a city: {name, country, admin, population, lat, lon, zone}; the biggest of that name unless qualified like “Portland, US”

city("Tokyo")
# → {name: "Tokyo", country: "Japan", admin: "Tokyo", population: 9733276, lat: 35.6895, lon: 139.692, zone: "Asia/Tokyo"}
city("Seattle").zone
# → "America/Los_Angeles"

See also: country, cities, nearest

countries

countries(): every country as a table

countries().len
# → 252
countries().filter(|c| c.continent == "Oceania").map(|c| c.name).take(3)
# → ["American Samoa", "Australia", "Cook Islands"]

See also: country, cities

cities

cities(): every city as a table, biggest first

cities()[0].name
# → "Shanghai"

See also: city, countries

great_circle

great_circle(from: str|map, to: str|map): great-circle distance between places

great_circle("Tokyo", "Paris")
# → 9712.52 km
great_circle("NYC", "LA") to mi
# → 2445.56 mi

See also: bearing, nearest

bearing

bearing(from: str|map, to: str|map): initial compass bearing from one place to another, clockwise from north

bearing("NYC", "London")
# → 51.2117 deg

See also: great_circle

nearest

nearest(lat: num, lon: num): the known city closest to a point

nearest(48.9, 2.3).name
# → "Paris"

See also: city, great_circle

More examples

geo

# currency of a country
country("Vietnam").currency
# → "VND"
# time in any city
date("2026-12-25 18:30") to "Seattle"
# → 2026-12-25 16:30:00 -08:00
# flight distance
great_circle("New York", "London") to mi
# → 3461.18 mi
# which way to Mecca
bearing("London", "Mecca")
# → 118.988 deg
# sunset in a city
sunset("Oslo", date(2026, 6, 21))
# → 2026-06-21 15:43:43 -05:00
# euro countries
countries().filter(|c| c.currency == "EUR").len
# → 36
# biggest cities in Japan
cities().filter(|c| c.country == "Japan").map(|c| c.name).take(3)
# → ["Tokyo", "Yokohama", "Osaka"]

dev

JWTs, URLs, semver, file permissions; networks, IDs, colors and codes below

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Submodules

moduleabout
netIP addresses, CIDR blocks, subnets, well-known ports
idsUUIDs, ULIDs and nano IDs; secure passwords and passphrases
colorscolors as “#rrggbb”: RGB/HSL, mixing, lightening, WCAG contrast
codescheck digits for cards, ISBNs and IBANs; HTTP status and MIME type lookups

Functions

functiondescription
jwt(s: str)decode a JWT into {header, payload, signature} without verifying it; exp, iat and nbf become dates
url_parse(s: str)split a URL into {scheme, host, port, path, query, fragment}; the query becomes a map, and repeated keys a list
url_build(m: map)the URL for a map like the one url_parse makes; missing parts are left out
semver(s: str)parse a semantic version into {major, minor, patch, pre, build}; compares by version precedence, also against strings
bump(v: str|map, part: str)next “major”, “minor” or “patch” version; a pre-release bumps to its own release
perm(mode: int|str)Unix permissions between octal and rwx: 755 or 0o755 gives “rwxr-xr-x”, and back

jwt

jwt(s: str): decode a JWT into {header, payload, signature} without verifying it; exp, iat and nbf become dates

jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZGEiLCJleHAiOjE5MjQ5OTIwMDB9.c2ln").payload.exp
# → 2030-12-31 18:00:00 -06:00

See also: decode, hmac

url_parse

url_parse(s: str): split a URL into {scheme, host, port, path, query, fragment}; the query becomes a map, and repeated keys a list

url_parse("https://example.com:8080/a/b?x=1&y=two%20words#top")
# → {scheme: "https", host: "example.com", port: 8080, path: "/a/b", query: {x: "1", y: "two words"}, fragment: "top"}

See also: url_build, encode

url_build

url_build(m: map): the URL for a map like the one url_parse makes; missing parts are left out

url_build({scheme: "https", host: "example.com", path: "/search", query: {q: "a b"}})
# → "https://example.com/search?q=a%20b"

See also: url_parse

semver

semver(s: str): parse a semantic version into {major, minor, patch, pre, build}; compares by version precedence, also against strings

semver("1.2.3") < semver("1.10.0")
# → true
semver("1.0.0-beta") < "1.0.0"
# → true
semver("v2.1.0-rc.1+build5")
# → {major: 2, minor: 1, patch: 0, pre: "rc.1", build: "build5"}

See also: bump

bump

bump(v: str|map, part: str): next “major”, “minor” or “patch” version; a pre-release bumps to its own release

bump("1.2.3", "minor")
# → "1.3.0"
bump("2.0.0-rc.1", "major")
# → "2.0.0"

See also: semver

perm

perm(mode: int|str): Unix permissions between octal and rwx: 755 or 0o755 gives “rwxr-xr-x”, and back

perm(755)
# → "rwxr-xr-x"
perm("rw-r--r--")
# → 0o644
perm(0o4755)
# → "rwsr-xr-x"

More examples

dev

# what's in this token?
jwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZGEiLCJpYXQiOjE1MTYyMzkwMjJ9.c2ln").payload
# → {sub: "ada", iat: 2018-01-17 19:30:22 -06:00}
# query string of a URL
url_parse("https://example.com/search?q=zil&page=2").query
# → {q: "zil", page: "2"}
# is 1.10 newer than 1.9?
semver("1.10.0") > "1.9.3"
# → true
# chmod 640 means
perm(640)
# → "rw-r-----"

dev.net

IP addresses, CIDR blocks, subnets, well-known ports

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
ip(v: str|int)an IPv4 or IPv6 address as an integer, or an integer as an address
cidr(s: str)an IPv4 block’s network, broadcast, netmask, host range and count
in_cidr(ip: str, block: str)whether an IPv4 address is inside a CIDR block
subnets(block: str, prefix: int)split an IPv4 block into smaller blocks
ip_kind(ip: str)loopback, private, link-local, multicast, unspecified or public
port(n: int) / port(name: str)the service on a well-known port, or the port for a service; nil if unknown

ip

ip(v: str|int): an IPv4 or IPv6 address as an integer, or an integer as an address

ip("192.168.1.5")
# → 3232235781
ip(3232235781)
# → "192.168.1.5"
ip("::1")
# → 1

See also: cidr, ip_kind

cidr

cidr(s: str): an IPv4 block’s network, broadcast, netmask, host range and count

cidr("192.168.1.77/26")
# → {network: "192.168.1.64", broadcast: "192.168.1.127", netmask: "255.255.255.192", prefix: 26, first: "192.168.1.65", last: "192.168.1.126", hosts: 62}

See also: in_cidr, subnets

in_cidr

in_cidr(ip: str, block: str): whether an IPv4 address is inside a CIDR block

in_cidr("10.0.3.7", "10.0.0.0/22")
# → true

See also: cidr

subnets

subnets(block: str, prefix: int): split an IPv4 block into smaller blocks

subnets("10.0.0.0/24", 26)
# → ["10.0.0.0/26", "10.0.0.64/26", "10.0.0.128/26", "10.0.0.192/26"]

See also: cidr

ip_kind

ip_kind(ip: str): loopback, private, link-local, multicast, unspecified or public

ip_kind("10.1.2.3")
# → "private"
ip_kind("8.8.8.8")
# → "public"

See also: ip

port

port(n: int) / port(name: str): the service on a well-known port, or the port for a service; nil if unknown

port(22)
# → "ssh"
port("postgresql")
# → 5432

See also: http_status, ip

More examples

net

# hosts in a /22
cidr("10.0.0.0/22").hosts
# → 1022
# is it in the block?
in_cidr("10.0.3.7", "10.0.0.0/22")
# → true
# address as bits
ip("192.168.1.5") to bin
# → 0b11000000101010000000000100000101
# next address
ip(ip("10.0.0.255") + 1)
# → "10.0.1.0"
# split into /24s
subnets("10.0.0.0/22", 24)
# → ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
# what runs on 5432?
port(5432)
# → "postgresql"

dev.ids

UUIDs, ULIDs and nano IDs; secure passwords and passphrases

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
uuid()random v4 UUID
password(len?: int)a password of letters, digits and symbols (default 20 characters, about 6.5 bits each), from the OS’s secure generator
passphrase(words?: int)random words joined by dashes (default 6, about 9.4 bits per word), from the OS’s secure generator
ulid()a ULID: 26 characters that sort by creation time, then 80 random bits
nanoid(len?: int)a URL-safe random ID (default 21 characters, like a UUID’s strength)
uuid_info(s: str)version and variant of a UUID, plus its timestamp for v1 and v7

uuid

uuid(): random v4 UUID

uuid()
# → "ca449926-a156-44c0-969e-a91a6575b974"

See also: uuid_info, ulid

password

password(len?: int): a password of letters, digits and symbols (default 20 characters, about 6.5 bits each), from the OS’s secure generator

password()
# → "7=oDbbGNbMFarp!R$pAq"
password(12)
# → "7+CLAbaD^&WC"

See also: passphrase

passphrase

passphrase(words?: int): random words joined by dashes (default 6, about 9.4 bits per word), from the OS’s secure generator

passphrase()
# → "diver-vocal-swamp-eagle-radio-lotus"
passphrase(4)
# → "focus-argue-drink-bulb"

See also: password

ulid

ulid(): a ULID: 26 characters that sort by creation time, then 80 random bits

ulid()
# → "01M4DSGJSK49SVAME8JNMHQSJJ"

See also: uuid, nanoid

nanoid

nanoid(len?: int): a URL-safe random ID (default 21 characters, like a UUID’s strength)

nanoid()
# → "dcecD0JN2UQg7bZKAnT-Z"
nanoid(8)
# → "HDMzFW6a"

See also: uuid, ulid

uuid_info

uuid_info(s: str): version and variant of a UUID, plus its timestamp for v1 and v7

uuid_info(uuid())
# → {version: 4, variant: "RFC 9562"}
uuid_info("c232ab00-9414-11ec-b3c8-9f6bdeced846")
# → {version: 1, variant: "RFC 9562", timestamp: 2022-02-22 13:22:22 -06:00}

See also: uuid

More examples

ids

# UUID
uuid()
# → "d4dc5c1c-d6f6-44f7-9029-cdaf2c70834f"
# when was this UUID made?
uuid_info("01890a5d-ac96-774b-bcce-b302099a8057").timestamp
# → 2023-06-29 22:34:18 -05:00
# sortable IDs
ulid()
# → "01M4DSGJSM6MK7S20FN7QPEC97"
# a strong password
password()
# → "@3K*8K3VseVBfym8@=nf"
# one you can type
passphrase()
# → "chimp-dice-elbow-chain-noble-bunny"

dev.colors

colors as “#rrggbb”: RGB/HSL, mixing, lightening, WCAG contrast

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
color(c: str|list)a color as “#rrggbb” from a name, “#rgb”, “#rrggbb” or [r, g, b]
rgb(r: num, g: num, b: num) / rgb(c: str|list)make a color from 0-255 channels, or get a color’s channels
hsl(h: num, s: num, l: num) / hsl(c: str|list)make a color from hue (degrees), saturation and lightness (0-100), or get them
mix(a: str|list, b: str|list, t?: num)blend from a (t = 0) to b (t = 1), halfway by default
lighten(c: str|list, amount: num)raise HSL lightness by amount (0-1)
darken(c: str|list, amount: num)lower HSL lightness by amount (0-1)
grayscale(c: str|list)the gray with the same perceived brightness
contrast(a: str|list, b: str|list)WCAG contrast ratio, 1 to 21; text wants 4.5+
swatch(c: str|list)a colored block plus the hex, for truecolor terminals

color

color(c: str|list): a color as “#rrggbb” from a name, “#rgb”, “#rrggbb” or [r, g, b]

color("tomato")
# → "#ff6347"
color("#f80")
# → "#ff8800"
color([255, 0, 128])
# → "#ff0080"

See also: rgb, hsl

rgb

rgb(r: num, g: num, b: num) / rgb(c: str|list): make a color from 0-255 channels, or get a color’s channels

rgb(255, 136, 0)
# → "#ff8800"
"orange".rgb
# → [255, 165, 0]

See also: hsl, color

hsl

hsl(h: num, s: num, l: num) / hsl(c: str|list): make a color from hue (degrees), saturation and lightness (0-100), or get them

hsl(210, 80, 50)
# → "#1980e6"
"teal".hsl
# → [180, 100, 25]

See also: rgb, color

mix

mix(a: str|list, b: str|list, t?: num): blend from a (t = 0) to b (t = 1), halfway by default

mix("red", "blue")
# → "#800080"
mix("white", "black", 25%)
# → "#bfbfbf"

See also: lighten, darken

lighten

lighten(c: str|list, amount: num): raise HSL lightness by amount (0-1)

lighten("#3478f6", 20%)
# → "#96b9fa"

See also: darken, mix

darken

darken(c: str|list, amount: num): lower HSL lightness by amount (0-1)

darken("#3478f6", 20%)
# → "#0847bc"

See also: lighten, mix

grayscale

grayscale(c: str|list): the gray with the same perceived brightness

grayscale("tomato")
# → "#969696"

See also: invert

contrast

contrast(a: str|list, b: str|list): WCAG contrast ratio, 1 to 21; text wants 4.5+

contrast("white", "black")
# → 21
contrast("#777", "white")
# → 4.48

See also: swatch

swatch

swatch(c: str|list): a colored block plus the hex, for truecolor terminals

swatch("tomato")
# →      #ff6347
["red", "gold", "teal"].map(swatch).join(" ")
# →      #ff0000      #ffd700      #008080

See also: color

More examples

colors

# named to hex
color("rebeccapurple")
# → "#663399"
# halfway between
mix("red", "blue")
# → "#800080"
# hover shade
darken("#3478f6", 10%)
# → "#0a5aed"
# readable text?
contrast("white", "#777")
# → 4.48
# a palette
(0..5).map(|i| hsl(i * 72, 70, 50))
# → ["#d92626", "#b5d926", "#26d96e", "#266ed9", "#b526d9"]

dev.codes

check digits for cards, ISBNs and IBANs; HTTP status and MIME type lookups

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
luhn(s: str|int)whether the Luhn check digit is right, as on credit cards and IMEIs; spaces and dashes are ignored
isbn(s: str)whether an ISBN-10 or ISBN-13 has a valid check digit
iban(s: str)whether an IBAN passes its mod-97 check
http_status(code: int)the reason phrase for an HTTP status code, or nil
mime(name: str)the MIME type for a file name or extension, or nil

luhn

luhn(s: str|int): whether the Luhn check digit is right, as on credit cards and IMEIs; spaces and dashes are ignored

luhn("4111 1111 1111 1111")
# → true
luhn(79927398710)
# → false

See also: isbn, iban

isbn

isbn(s: str): whether an ISBN-10 or ISBN-13 has a valid check digit

isbn("0-306-40615-2")
# → true
isbn("9780306406157")
# → true

See also: luhn

iban

iban(s: str): whether an IBAN passes its mod-97 check

iban("GB82 WEST 1234 5698 7654 32")
# → true

See also: luhn

http_status

http_status(code: int): the reason phrase for an HTTP status code, or nil

http_status(404)
# → "Not Found"
(200..=204).map(http_status)
# → ["OK", "Created", "Accepted", "Non-Authoritative Information", "No Content"]

See also: mime

mime

mime(name: str): the MIME type for a file name or extension, or nil

mime("png")
# → "image/png"
mime("data.json")
# → "application/json"

See also: ext

More examples

codes

# a typo in a card number?
luhn("4111 1111 1111 1112")
# → false
# valid ISBN?
isbn("978-0-306-40615-7")
# → true
# what's a 418?
http_status(418)
# → "I'm a teapot"
# Content-Type for a file
mime("report.pdf")
# → "application/pdf"

art

clip art, gradients, QR codes, images, animation; frames, fractals, games below

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

img modes

# "blocks" (default): two pixels per character in full color, for truecolor terminals
# "ascii": brightness as " .:-=+*#%@", pastes anywhere
# "braille": Floyd–Steinberg dithered dots, the sharpest in black and white

animate

# a glider crossing the screen: animate(|i| life("glider", i), 8, 40)
# a fractal growing: animate(|i| fractal("tree", i + 1, 40), 2, 7)

Submodules

moduleabout
framesboxes, cowsay; art side by side, stacked, overlaid, mirrored, rotated, scaled
generativeL-systems, fractals, the Mandelbrot set, cellular automata, mazes, starfields
gameschess boards from FEN, playing cards, dice faces
figletbig-letter banners in FIGlet fonts like slant and shadow, or any .flf file

Functions

functiondescription
clipart(name?: str)a piece from the gallery, or the list of names
rainbow(s: str)color each character along a diagonal rainbow, for truecolor terminals
gradient(s: str, from: str|list, to: str|list)fade text left to right between two colors, for truecolor terminals
qr(text: str, ec?: str)a scannable QR code in half blocks, light on dark; ec is the error correction level “L”, “M” (default), “Q” or “H”
segments(s: str|int)big seven-segment digits for 0-9, A-F, - . : and spaces
img(path: str, width?: int, mode?: str)draw an image file width characters wide (default: the terminal’s, or 80); mode is “blocks” (default), “ascii” or “braille”
animate(frames: list|fn, fps?: num, count?: int)draw frames one after another in place, fps a second (default 10). A list plays count times (default once); a function gets the frame number from 0 and plays count frames (default 60). Off a terminal only the last frame prints

clipart

clipart(name?: str): a piece from the gallery, or the list of names

clipart("owl")
# →  ,_,
# → (O,O)
# → (   )
# → -"-"-
clipart().len
# → 17

See also: cowsay, boxed

rainbow

rainbow(s: str): color each character along a diagonal rainbow, for truecolor terminals

"rainbow".rainbow
# → rainbow

See also: gradient, strip_ansi

gradient

gradient(s: str, from: str|list, to: str|list): fade text left to right between two colors, for truecolor terminals

gradient("sunset", "gold", "crimson")
# → sunset

See also: rainbow, color

qr

qr(text: str, ec?: str): a scannable QR code in half blocks, light on dark; ec is the error correction level “L”, “M” (default), “Q” or “H”

qr("zil")
# → █████████████████████████████
# → █████████████████████████████
# → ████ ▄▄▄▄▄ █▄▀▄███ ▄▄▄▄▄ ████
# → ████ █   █ █▀▄██ █ █   █ ████
# → ████ █▄▄▄█ █ █ ▀▄█ █▄▄▄█ ████
# → ████▄▄▄▄▄▄▄█ █▄▀ █▄▄▄▄▄▄▄████
# → ████ ▀██ █▄▄▄█▀ ▀▄▄ ▄▄█▀▄████
# → ████▀▄▄▀▀▀▄ █▄▀▄█▀ ▄ ██▄▀████
# → ████████▄█▄█  ██   ██▀█▄█████
# → ████ ▄▄▄▄▄ █▄█ ▀ ▄█▀█ █ ▀████
# → ████ █   █ █▄ ▀ ▀█▄ ▄▀▄  ████
# → ████ █▄▄▄█ ██▄▀▄█▀ ▄ ▄ ██████
# → ████▄▄▄▄▄▄▄█▄▄▄█▄▄▄███▄█▄████
# → █████████████████████████████
# → ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀

See also: url_build

segments

segments(s: str|int): big seven-segment digits for 0-9, A-F, - . : and spaces

segments("12:45")
# →       ▄▄          ▄▄
# →    █  ▄▄█ • █▄▄█ █▄▄
# →    █ █▄▄  •    █  ▄▄█
segments("C0FFEE")
# →  ▄▄   ▄▄   ▄▄   ▄▄   ▄▄   ▄▄
# → █    █  █ █▄▄  █▄▄  █▄▄  █▄▄
# → █▄▄  █▄▄█ █    █    █▄▄  █▄▄

See also: banner, clock

img

img(path: str, width?: int, mode?: str): draw an image file width characters wide (default: the terminal’s, or 80); mode is “blocks” (default), “ascii” or “braille”

img("cat.png")
img("logo.jpg", 40, "ascii")
img("photo.png", 60, "braille").negative

See also: negative, boxed

animate

animate(frames: list|fn, fps?: num, count?: int): draw frames one after another in place, fps a second (default 10). A list plays count times (default once); a function gets the frame number from 0 and plays count frames (default 60). Off a terminal only the last frame prints

animate(|i| life("glider", i), 8, 40)
animate(["|", "/", "-", "\\"], 12, 5)

See also: life, fractal

More examples

# what's in it
clipart()
# → ["cat", "dog", "owl", "bunny", "fish", "duck", "bat", "snail", "crab", "ghost", "skull", "coffee", "rocket", "house", "tree", "cactus", "heart"]
# a cat in a box
clipart("cat").boxed("round")
# → ╭─────────╮
# → │  /\_/\  │
# → │ ( o.o ) │
# → │  > ^ <  │
# → ╰─────────╯
# pride
banner("zil", "slant").rainbow
# →         _ __
# →  ____  (_) /
# → /_  / / / /
# →  / /_/ / /
# → /___/_/_/
# measure colored art
rainbow("hi").strip_ansi.len
# → 2
# a link for your phone
qr("https://github.com/zanbowie138/zil")
# → █████████████████████████████████████
# → █████████████████████████████████████
# → ████ ▄▄▄▄▄ █▀  ▀█▀▀▄   ███ ▄▄▄▄▄ ████
# → ████ █   █ █ ▄▀█▀▄█▄▀▀▄  █ █   █ ████
# → ████ █▄▄▄█ ██ ▀████ ▄ ▄▄ █ █▄▄▄█ ████
# → ████▄▄▄▄▄▄▄█ █ █ █ ▀ █▄█▄█▄▄▄▄▄▄▄████
# → ████ ▀▀█▀█▄▀ ▄█▀▄▀██▀▄█▀█ ▄▀▀▄  █████
# → ████▀█▄ ▀▀▄▀ ▄▀█▀ ▄▄▀▄█▀█▀██▄▀███████
# → ████▄██▀▄▀▄▄ ▀▄▀ ▄█▀ ▀▀▀▀█  ▀██▀ ████
# → █████▄▀▄█ ▄█▄ █ ███▄ █▄▀▄▄█▀ ▄ █▀████
# → ████ █▀ ▀▄▄▀▄▀▄▀█▀▀▀▀▄▀ █▄▀ ▀▄ ▀▀████
# → ████ ▄ █▀▄▄█▀▄██▀▀█ ▄▄█▄▀█▀█▄▄██▄████
# → ████▄██▄▄▄▄█ ▀█▀██ ▀▄▄▄▄ ▄▄▄ ▀▄▄▄████
# → ████ ▄▄▄▄▄ ██ ▄ ▀█▀  █▄  █▄█ ▄▄█▀████
# → ████ █   █ ██▀▄ ▀  ▀▄█▄   ▄▄▄ ▀▄ ████
# → ████ █▄▄▄█ ██▄ ▄▀▀▄▄▀▄▄▄▄▄  ▄  ▄▀████
# → ████▄▄▄▄▄▄▄█▄▄███▄██▄▄▄▄██▄▄▄█▄██████
# → █████████████████████████████████████
# → ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# a big clock
segments(now.format("%H:%M"))
# →  ▄▄   ▄▄     ▄▄   ▄▄
# → █  █    █ • █▄▄  █▄▄█
# → █▄▄█    █ •  ▄▄█ █▄▄█

art.frames

boxes, cowsay; art side by side, stacked, overlaid, mirrored, rotated, scaled

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
boxed(s: str, style?: str)draw a box around text; style is “single” (default), “double”, “round”, “heavy”, “ascii”, “stars” or “shadow”
cowsay(s: str, critter?: str)a critter saying s in a speech bubble; critter is “cow” (default), “cat”, “ghost” or “crab”
think(s: str, critter?: str)like cowsay, in a thought bubble
beside(a: str, b: str, gap?: int)put b to the right of a, tops aligned, gap spaces apart (default 1)
stack(a: str, b: str, align?: str)put b under a, aligned “left” (default), “center” or “right”
overlay(a: str, b: str, x: int, y: int)draw b over a with its top-left at column x, row y; spaces in b are see-through
mirror(s: str, axis?: str)flip left-right (“h”, default) or upside down (“v”), turning slashes and brackets to match
rotate(s: str, degrees: int)turn art clockwise by 90, 180 or 270 degrees
scale(s: str, n: int)make every character an n×n block of itself
negative(s: str)swap ink and background: spaces become blocks, blocks and braille dots invert
trim_art(s: str)drop blank lines at the ends, shared left indentation and trailing spaces

boxed

boxed(s: str, style?: str): draw a box around text; style is “single” (default), “double”, “round”, “heavy”, “ascii”, “stars” or “shadow”

boxed("hello")
# → ┌───────┐
# → │ hello │
# → └───────┘
"zil\ncalc".boxed("round")
# → ╭──────╮
# → │ zil  │
# → │ calc │
# → ╰──────╯

See also: cowsay, stack

cowsay

cowsay(s: str, critter?: str): a critter saying s in a speech bubble; critter is “cow” (default), “cat”, “ghost” or “crab”

cowsay("moo")
# →  _____
# → < moo >
# →  -----
# →    \
# →     \
# →          (__)
# →          (oo)
# →   /-------\/
# →  / |     ||
# → *  ||----||
# →    ~~    ~~

See also: think, boxed

think

think(s: str, critter?: str): like cowsay, in a thought bubble

think("hmm", "ghost")
# →  _____
# → ( hmm )
# →  -----
# →    o
# →     o
# →       .-.
# →      (o o)
# →      | O \
# →       \   \
# →        `~~~'

See also: cowsay

beside

beside(a: str, b: str, gap?: int): put b to the right of a, tops aligned, gap spaces apart (default 1)

"a\nb".beside("c\nd\ne")
# → a c
# → b d
# →   e

See also: stack, overlay

stack

stack(a: str, b: str, align?: str): put b under a, aligned “left” (default), “center” or “right”

"wide one".stack("x", "right")
# → wide one
# →        x

See also: beside

overlay

overlay(a: str, b: str, x: int, y: int): draw b over a with its top-left at column x, row y; spaces in b are see-through

boxed("     ").overlay("hi", 2, 1)
# → ┌───────┐
# → │ hi    │
# → └───────┘

See also: beside

mirror

mirror(s: str, axis?: str): flip left-right (“h”, default) or upside down (“v”), turning slashes and brackets to match

"(=^.^=)/".mirror
# → "\\(=^.^=)"
"/\\\n\\/".mirror("v")
# → /\
# → \/

See also: rotate, flip

rotate

rotate(s: str, degrees: int): turn art clockwise by 90, 180 or 270 degrees

"abc\ndef".rotate(90)
# → da
# → eb
# → fc

See also: mirror

scale

scale(s: str, n: int): make every character an n×n block of itself

"ab".scale(2)
# → aabb
# → aabb

See also: banner

negative

negative(s: str): swap ink and background: spaces become blocks, blocks and braille dots invert

"▀ █".negative
# → "▄█ "

See also: invert

trim_art

trim_art(s: str): drop blank lines at the ends, shared left indentation and trailing spaces

"\n   ab\n    c\n\n".trim_art
# → ab
# →  c

See also: dedent

More examples

compose

# a caption under a picture
clipart("owl").stack("hoo?", "center")
# →  ,_,
# → (O,O)
# → (   )
# → -"-"-
# → hoo?
# two friends
clipart("cat").beside(clipart("dog"), 4)
# →  /\_/\       __      _
# → ( o.o )    o'')}____//
# →  > ^ <      `_/      )
# →             (_(_/-(_/
# a cat says
cowsay("deploy on friday?", "cat")
# →  ___________________
# → < deploy on friday? >
# →  -------------------
# →    \
# →     \
# →       /\_/\
# →      ( o.o )
# →       > ^ <
# a picture frame
clipart("house").boxed("double").boxed("shadow")
# → ┌──────────────┐
# → │ ╔══════════╗ │▒
# → │ ║     /\   ║ │▒
# → │ ║    /  \  ║ │▒
# → │ ║   /____\ ║ │▒
# → │ ║   | [] | ║ │▒
# → │ ║   |_||_| ║ │▒
# → │ ╚══════════╝ │▒
# → └──────────────┘▒
# →  ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
# a dark sky
starfield(20, 4, 1).negative
# → ████ █████ █ ███████
# → ██████████████ █████
# → █████ ██████ ██████ 

art.generative

L-systems, fractals, the Mandelbrot set, cellular automata, mazes, starfields

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
lsystem(axiom: str, rules: map, angle: num, n: int, width?: int)rewrite axiom n times with rules (one character → its replacement), then draw it with a turtle in braille, width characters wide (default 60). F and G draw a step, f steps without drawing, + and - turn by angle degrees, | turns around, [ and ] save and restore position; other characters do nothing. Starts facing up
fractal(name?: str, n?: int, width?: int)a named L-system fractal: “dragon”, “koch”, “hilbert”, “sierpinski”, “levy”, “gosper”, “tree”, “fern” or “bush”; n is the depth, width as in lsystem. No name lists them
rule(n: int, width?: int, steps?: int)an elementary cellular automaton, 0-255, grown from one cell for steps rows (default width / 2) on a wrapping row of width cells (default 64); two rows per line
spiral(size?: int)an Ulam spiral: 1, 2, 3… wound out from the center with the primes as dots, which line up on diagonals; size characters wide (default 40)
starfield(width: int, height: int, seed?: int)a random night sky; the same seed gives the same sky
mandelbrot(width?: int, height?: int)the Mandelbrot set in ASCII, default 72×24
life(start: str, steps?: int)run Conway’s Game of Life for steps (default 1) and draw the live cells; start is rows of # and . or one of “glider”, “blinker”, “pulsar”, “r_pentomino”
maze(width?: int, height?: int)a random perfect maze (one path between any two cells), default 20×10 cells

lsystem

lsystem(axiom: str, rules: map, angle: num, n: int, width?: int): rewrite axiom n times with rules (one character → its replacement), then draw it with a turtle in braille, width characters wide (default 60). F and G draw a step, f steps without drawing, + and - turn by angle degrees, | turns around, [ and ] save and restore position; other characters do nothing. Starts facing up

lsystem("F", {F: "F+F-F-F+F"}, 90, 3, 30)
# →              ⡤⠇
# →            ⣀⡏⣏⡇
# →         ⢀⣀⣖⡃
# →       ⢀⣰⠚ ⠓⣗⣆⣖⡆
# →      ⢰⢺⢲   ⡖⡗⡗⡆
# →    ⢠⠼⠉⠈⠉   ⠉⠁⠉⠁
# →  ⡤⠏⠹⠽
# → ⣏⡁
# →  ⠓⣆⣰⣲
# →    ⠘⢲⣀⢀⣀   ⣀⡀⣀⡀
# →      ⠸⢼⠼   ⠧⡧⡧⠇
# →       ⠈⠹⢤ ⡤⡯⠏⠯⠇
# →         ⠈⠉⠯⡅
# →            ⠉⣇⣏⡇
# →              ⠓⡆

See also: fractal

fractal

fractal(name?: str, n?: int, width?: int): a named L-system fractal: “dragon”, “koch”, “hilbert”, “sierpinski”, “levy”, “gosper”, “tree”, “fern” or “bush”; n is the depth, width as in lsystem. No name lists them

fractal("koch", 3, 30)
# →           ⡀⠠⡴⢧⠄⢀
# →         ⠐⡞⠑⠚  ⠓⠊⢓⡆
# →    ⣀⣠⣀  ⢈⣙⠄    ⠠⣋⡁  ⣀⣠⣀
# → ⣀⡴⣀⡕ ⢪⣀⢦⣀⠇      ⠐⣅⡴⣀⡕ ⢪⣀⢦⣀
# → ⠯⡄                      ⢠⠽
# → ⢖⠋                      ⠙⡲
# → ⠓⠲⠒⡢                  ⢔⠒⠖⠚
# →    ⣉⡧                ⠰⣉
# → ⡤⠴⠤⠕                  ⠪⠤⠦⢤
# → ⠮⣄                      ⣠⠵
# → ⣖⠃                      ⠘⣲
# → ⠉⠳⠉⡣ ⢜⠉⠞⠉⡆      ⠠⡋⠳⠉⡣ ⢜⠉⠞⠉
# →    ⠉⠙⠉  ⢈⣩⠂    ⠐⣍⡁  ⠉⠙⠉
# →         ⠠⢧⡠⢤  ⡤⢄⡬⠇
# →           ⠁⠐⠳⡞⠂⠈
fractal()
# → ["dragon", "koch", "hilbert", "sierpinski", "levy", "gosper", "tree", "fern", "bush"]

See also: lsystem, rule

rule

rule(n: int, width?: int, steps?: int): an elementary cellular automaton, 0-255, grown from one cell for steps rows (default width / 2) on a wrapping row of width cells (default 64); two rows per line

rule(30, 32)
# →                ▄█▄
# →              ▄█▀▄▄█▄
# →            ▄█▀▄▄█▄ ▄█▄
# →          ▄█▀▄▄█▄  ▄█▄▄█▄
# →        ▄█▀▄▄█▄ ▄█▀▀▄   ▄█▄
# →      ▄█▀▄▄█▄  ▄█ █▀▀▀ █▀▄▄█▄
# →    ▄█▀▄▄█▄ ▄█▀▀▄▄█▀▄▄█▀ █▄ ▄█▄
# →  ▄█▀▄▄█▄  ▄█ █▀▀▄ ▄█▄▄█▀▀▄▄█▄▄█▄
rule(90, 32, 16)
# →                ▄▀▄
# →              ▄▀▄ ▄▀▄
# →            ▄▀▄     ▄▀▄
# →          ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄
# →        ▄▀▄             ▄▀▄
# →      ▄▀▄ ▄▀▄         ▄▀▄ ▄▀▄
# →    ▄▀▄     ▄▀▄     ▄▀▄     ▄▀▄
# →  ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄

See also: life, fractal

spiral

spiral(size?: int): an Ulam spiral: 1, 2, 3… wound out from the center with the primes as dots, which line up on diagonals; size characters wide (default 40)

spiral(20)
# → ⢀⠄⠁⢐⢄⠄⠐ ⠔⠑⠄⠕   ⢁ ⠁⢐
# → ⢀⠄ ⢀⠁⢁  ⠁⠔⠁⢔⢐ ⠐⢄⠁⠄⠄⠁
# → ⠐⠑⢄⠁⢁⠄ ⠕⠁⢀ ⢅⢀⠐⢅⢐⢀ ⠐⠐
# → ⢀⠁⢁⠐⢀ ⢑⢀⠐⢑⠄⠄ ⢀⠅⢁⢄⠑⠄⠄
# →  ⠐⠄⠄⠄⠔⠄⠔⠔⢄⢁⢕⠐⠑ ⠄⠐⢐⠐
# →   ⢀⠐⢀⢀ ⢑⢔⠑⢌⠁⢁⢁⠁⢀⠁⢁⢁
# → ⢀⢅  ⢁ ⢅⠄⠑ ⠄⠁⢄ ⠕⠁⢀⠁⠄⢄
# →   ⠅⢄⠄⢅ ⠐⠑ ⠕⢅⢀⠐⠄⠐ ⠄⠔
# →  ⠑⠐⠁⠁⢀⠐⢅⠄ ⢅⠔⠁⢁ ⠐⠄ ⢐⢅
# → ⢐ ⢐⢀⠁⢁⠐ ⢁   ⠐ ⢐⠄ ⠑⠄

See also: is_prime

starfield

starfield(width: int, height: int, seed?: int): a random night sky; the same seed gives the same sky

starfield(30, 4, 7)
# →            .
# →     ·            ·
# →             · ·   + +
# →              . ·

See also: negative

mandelbrot

mandelbrot(width?: int, height?: int): the Mandelbrot set in ASCII, default 72×24

mandelbrot(40, 12)
# → 
# →                           ...:
# →                        ...:@@@..
# →                     ..#+=@@@@@@@:#:.
# →             .........#@@@@@@@@@@@@-.
# →            ..:@@@@@:@@@@@@@@@@@@@@@-
# →    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@-..
# →            ..:@@@@@:@@@@@@@@@@@@@@@-
# →             .........#@@@@@@@@@@@@-.
# →                     ..#+=@@@@@@@:#:.
# →                        ...:@@@..
# →                           ...:

See also: life

life

life(start: str, steps?: int): run Conway’s Game of Life for steps (default 1) and draw the live cells; start is rows of # and . or one of “glider”, “blinker”, “pulsar”, “r_pentomino”

life("blinker")
# → #
# → #
# → #
life("glider", 4)
# → .#.
# → ..#
# → ###

See also: maze

maze

maze(width?: int, height?: int): a random perfect maze (one path between any two cells), default 20×10 cells

maze(6, 3)
# → ██  ██████████████████████
# → ██  ██          ██      ██
# → ██  ██  ██████  ██  ██  ██
# → ██      ██      ██  ██  ██
# → ██████████  ██████  ██  ██
# → ██                  ██  ██
# → ██████████████████████  ██

See also: life

More examples

grow things

# a dragon curve
fractal("dragon", 8, 30)
# →                    ⣀⣸⣉⡇ ⢀⣸⣉⡇
# →                    ⠓⢺⠒⡆⡖⢺⢺⠒⡆⡖⠂
# →   ⠸⠭⡧⠏⠹⢤       ⢠⢤  ⠯⢽⠭⡯⡅⠈⠉ ⠉⠯⠅
# → ⣖⣲⣰⣒⡃ ⠐⠚      ⣖⣺⣚ ⣀⣖⣚ ⠓⠃   ⣆⡖⠂
# →  ⠸⢼⠤⠇         ⡤⢼⢼⠤⡧⡧⢼⠤⡄
# → ⣀⣀⢘⣒⣆⣖⣲  ⣀⣰⣲  ⣓⣺⣺⣒⣗⣗⣺⠒⣗⡆
# → ⠧⢼⢼⠤⡧⡧⢤⢠⠤⡧⢼⢤ ⡤⡧⢼⢼⠤⡧⡧⢤
# →  ⠈⠉ ⠉⠁⠈⢹⣉⣏⣹⣹⣉⡁⠉⠉⠈⠉⣏⣏⣹⣉⣇⡀
# →      ⡖⢲⢰⠒⡗⠚⠘⠒⠃ ⢰⢲ ⡖⡗⠚ ⠓⠃
# →      ⠉⠹⠽⠉⠯⠽    ⠈⠹⠭⠏⠯⠽
# a tree in a box
fractal("tree", 6, 24).boxed("round")
# → ╭───────────────────╮
# → │  ⢀⡀⠐⢾ ⡯⠂ ⠐⠪⡇⡹⠒ ⣀  │
# → │ ⣀⠓⣇  ⢹     ⢹⠁ ⢀⠗⢃ │
# → │ ⠞⠉⠉⠑⠤⣸     ⢸⡠⠔⠉⠉⠱ │
# → │      ⠘⢆   ⢀⠎      │
# → │       ⠈⢢ ⣠⠃       │
# → │         ⢳⠃        │
# → │         ⢸         │
# → │         ⢸         │
# → │         ⢸         │
# → │         ⢸         │
# → │         ⢸         │
# → │         ⢸         │
# → ╰───────────────────╯
# Sierpinski, two ways
rule(90, 32, 16).beside(fractal("sierpinski", 4, 16), 4)
# →                ▄▀▄                         ⣰⣇
# →              ▄▀▄ ▄▀▄                      ⣼⣷⣾⣧
# →            ▄▀▄     ▄▀▄                  ⢀⡾⢿⡄⢠⡿⢷⡀
# →          ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄               ⢠⣞⠛⠚⠛⠛⠓⠛⣳⡄
# →        ▄▀▄             ▄▀▄            ⣠⣿⣻⣦    ⣴⣟⣿⣄
# →      ▄▀▄ ▄▀▄         ▄▀▄ ▄▀▄         ⣰⣯⡉⠉⣹⣧⡀⢀⣼⣏⠉⢉⣽⣆
# →    ▄▀▄     ▄▀▄     ▄▀▄     ▄▀▄      ⠼⠷⠼⠷⠼⠷⠼⠷⠾⠧⠾⠧⠾⠧⠾⠧
# →  ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄
# invent a plant
lsystem("X", {X: "F[-X][X]F[-X]+FX", F: "FF"}, 25, 4, 30)
# → ⣀⡀⠐⣄⡀ ⠈⢧ ⡄
# →  ⠈⠉⠙⠳⡀ ⠈⢷⡇    ⠒⡄⢀
# →      ⠈⠲⣄⢻⢆⣜    ⠸⣸
# →         ⠹⣿⠎     ⡟  ⢰
# →         ⡀⠹⣀   ⠢⡌⣇ ⢠⣏⡄
# →         ⠉⠓⢵⣇  ⠠⡸⣿⣜⠟⡉
# →            ⢻   ⣷⣿⡿⠊⢱
# →            ⠈⢣ ⣬⡿⡇ ⢠⣏⡀
# →              ⢣⡇⣧⣏⣼⡟⠋
# →              ⢸⡝⢺⣵⣿⠖⠁
# →              ⢸⢠⠋
# →              ⢸⠃
# →              ⢸
# →              ⢸
# →              ⢸
# the Mandelbrot set
mandelbrot()
# → 
# →                                                .-.
# →                                                .......
# →                                               ...-=:...
# →                                           .....:@@@@@:..
# →                                       .........:@@@@+:.........
# →                                    ....:@@:%@@@@@@@@@@@@@:.:::#.
# →                                 .....*.#@@@@@@@@@@@@@@@@@@@@@:..
# →                      ................-@@@@@@@@@@@@@@@@@@@@@@@=...
# →                     ....:@-.:@::...:=@@@@@@@@@@@@@@@@@@@@@@@@@-..
# →                    ...:.-@@@@@@@@=:-@@@@@@@@@@@@@@@@@@@@@@@@@@@-.
# →             ........#=:+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.
# →      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+:...
# →             ........#=:+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.
# →                    ...:.-@@@@@@@@=:-@@@@@@@@@@@@@@@@@@@@@@@@@@@-.
# →                     ....:@-.:@::...:=@@@@@@@@@@@@@@@@@@@@@@@@@-..
# →                      ................-@@@@@@@@@@@@@@@@@@@@@@@=...
# →                                 .....*.#@@@@@@@@@@@@@@@@@@@@@:..
# →                                    ....:@@:%@@@@@@@@@@@@@:.:::#.
# →                                       .........:@@@@+:.........
# →                                           .....:@@@@@:..
# →                                               ...-=:...
# →                                                .......
# →                                                .-.
# a glider, 8 steps on
life("glider", 8)
# → .#.
# → ..#
# → ###
# draw your own
life(".#.\n.#.\n.#.", 1)
# → "###"
# a maze
maze(12, 6)
# → ██  ██████████████████████████████████████████████
# → ██          ██                      ██          ██
# → ██████████  ██████  ██████████████  ██  ██████  ██
# → ██      ██      ██      ██      ██  ██      ██  ██
# → ██  ██  ██████  ██  ██  ██████  ██  ██████  ██  ██
# → ██  ██      ██  ██  ██  ██      ██      ██  ██  ██
# → ██  ██  ██████  ██████  ██  ██  ██████  ██████  ██
# → ██  ██      ██      ██  ██  ██      ██          ██
# → ██  ██████  ██████  ██  ██  ██████████████████  ██
# → ██  ██  ██      ██      ██      ██          ██  ██
# → ██  ██  ██████  ██████████  ██  ██  ██  ██████  ██
# → ██          ██              ██      ██          ██
# → ██████████████████████████████████████████████  ██

art.games

chess boards from FEN, playing cards, dice faces

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
chess(fen?: str)a chess board from the piece part of a FEN string; the starting position by default
card(c: str)a playing card like “A♠”, “10h” or “qd” (suits ♠♥♦♣ or s h d c); “?” is the back
cards(hand: list)playing cards side by side
dice(faces: int|list)die faces with pips, 1-6, side by side for a list

chess

chess(fen?: str): a chess board from the piece part of a FEN string; the starting position by default

chess()
# → 8 ♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
# → 7 ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
# → 6   ·   ·   ·   ·
# → 5 ·   ·   ·   ·  
# → 4   ·   ·   ·   ·
# → 3 ·   ·   ·   ·  
# → 2 ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
# → 1 ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
# →   a b c d e f g h
chess("8/8/8/4k3/8/8/4P3/4K3 w - - 0 1")
# → 8   ·   ·   ·   ·
# → 7 ·   ·   ·   ·  
# → 6   ·   ·   ·   ·
# → 5 ·   ·   ♚   ·  
# → 4   ·   ·   ·   ·
# → 3 ·   ·   ·   ·  
# → 2   ·   · ♙ ·   ·
# → 1 ·   ·   ♔   ·  
# →   a b c d e f g h

See also: card

card

card(c: str): a playing card like “A♠”, “10h” or “qd” (suits ♠♥♦♣ or s h d c); “?” is the back

card("Q♥")
# → ┌─────┐
# → │Q    │
# → │  ♥  │
# → │    Q│
# → └─────┘

See also: cards, dice

cards

cards(hand: list): playing cards side by side

cards(["As", "Kh", "?"])
# → ┌─────┐ ┌─────┐ ┌─────┐
# → │A    │ │K    │ │░░░░░│
# → │  ♠  │ │  ♥  │ │░░░░░│
# → │    A│ │    K│ │░░░░░│
# → └─────┘ └─────┘ └─────┘

See also: card

dice

dice(faces: int|list): die faces with pips, 1-6, side by side for a list

dice(5)
# → ┌───────┐
# → │ ●   ● │
# → │   ●   │
# → │ ●   ● │
# → └───────┘
dice([1, 2, 3])
# → ┌───────┐ ┌───────┐ ┌───────┐
# → │       │ │     ● │ │ ●     │
# → │   ●   │ │       │ │   ●   │
# → │       │ │ ●     │ │     ● │
# → └───────┘ └───────┘ └───────┘

See also: roll, card

More examples

games

# what you rolled
dice(roll("3d6").rolls)
# → ┌───────┐ ┌───────┐ ┌───────┐
# → │ ●   ● │ │ ●   ● │ │     ● │
# → │       │ │   ●   │ │       │
# → │ ●   ● │ │ ●   ● │ │ ●     │
# → └───────┘ └───────┘ └───────┘
# a royal flush
cards(["10♠", "J♠", "Q♠", "K♠", "A♠"])
# → ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
# → │10   │ │J    │ │Q    │ │K    │ │A    │
# → │  ♠  │ │  ♠  │ │  ♠  │ │  ♠  │ │  ♠  │
# → │   10│ │    J│ │    Q│ │    K│ │    A│
# → └─────┘ └─────┘ └─────┘ └─────┘ └─────┘
# the opening position
chess()
# → 8 ♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
# → 7 ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
# → 6   ·   ·   ·   ·
# → 5 ·   ·   ·   ·  
# → 4   ·   ·   ·   ·
# → 3 ·   ·   ·   ·  
# → 2 ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
# → 1 ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
# →   a b c d e f g h

art.figlet

big-letter banners in FIGlet fonts like slant and shadow, or any .flf file

Example results generated on 2026-10-08. Ones using now, today or randomness will differ when you run them: press Run on any example to run it in your browser, after editing it if you like.

Functions

functiondescription
banner(s: str, font?: str)big letters; font is “block” (default, A-Z, 0-9, some punctuation), one of fonts(), or a path to a FIGlet .flf file
fonts()the built-in banner fonts

banner(s: str, font?: str): big letters; font is “block” (default, A-Z, 0-9, some punctuation), one of fonts(), or a path to a FIGlet .flf file

banner("hi!")
# → ██  ██  ██████    ██
# → ██  ██    ██      ██
# → ██████    ██      ██
# → ██  ██    ██
# → ██  ██  ██████    ██
banner("zil", "slant")
# →         _ __
# →  ____  (_) /
# → /_  / / / /
# →  / /_/ / /
# → /___/_/_/

See also: fonts, segments, style

fonts

fonts(): the built-in banner fonts

fonts()
# → ["block", "standard", "small", "slant", "big", "mini", "smslant", "shadow", "smshadow", "script", "smscript", "lean", "banner", "bubble", "digital"]

See also: banner

More examples

banners

# a README header
banner("zil", "standard")
# →      _ _
# →  ___(_) |
# → |_  / | |
# →  / /| | |
# → /___|_|_|
# every font
fonts().map(|f| banner("Hi", f)).join("\n")
# → ██  ██  ██████
# → ██  ██    ██
# → ██████    ██
# → ██  ██    ██
# → ██  ██  ██████
# →  _   _ _
# → | | | (_)
# → | |_| | |
# → |  _  | |
# → |_| |_|_|
# →  _  _ _
# → | || (_)
# → | __ | |
# → |_||_|_|
# →     __  ___
# →    / / / (_)
# →   / /_/ / /
# →  / __  / /
# → /_/ /_/_/
# →  _    _ _
# → | |  | (_)
# → | |__| |_
# → |  __  | |
# → | |  | | |
# → |_|  |_|_|
# → 
# → |_|o
# → | ||
# →    __ ___
# →   / // (_)
# →  / _  / /
# → /_//_/_/
# →  |   |_)
# →  |   | |
# →  ___ | |
# → _|  _|_|
# →  |  |_)
# →  __ | |
# → _| _|_|
# →  ,
# → /|   | o
# →  |___|
# →  |   |\|
# →  |   |/|_/
# →  ,
# → /|  | o
# →  |--| |
# →  |  |)|/
# → 
# →     _/    _/  _/
# →    _/    _/
# →   _/_/_/_/  _/
# →  _/    _/  _/
# → _/    _/  _/
# → #     #
# → #     # #
# → #     # #
# → ####### #
# → #     # #
# → #     # #
# → #     # #
# →   _   _
# →  / \ / \
# → ( H | i )
# →  \_/ \_/
# → +-+-+
# → |H|i|
# → +-+-+
# a boxed title
banner("v2", "small").boxed("round")
# → ╭───────────╮
# → │      ___  │
# → │ __ _|_  ) │
# → │ \ V // /  │
# → │  \_//___| │
# → ╰───────────╯