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,todayor 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
| module | about |
|---|---|
| core | values, printing, type conversions, parsing, help |
| fs | files, directories, paths, JSON and CSV |
| sys | script arguments, environment variables, shell commands, HTTP GET |
| text | case, splitting, search, regex; encodings, ciphers and more below |
| text.layout | pad, center, wrap, truncate, dedent; snake_case, camelCase, slugs; words |
| text.compare | edit distance, similarity, the closest of a list, line-by-line diffs |
| text.translation | translate text between languages online; cached translations work offline |
| text.encoding | base64, base32, base58, URL and hex encodings, UTF-8 bytes, hex dumps |
| text.hash | SHA-1/256/512, MD5, HMAC and CRC-32 of strings |
| text.ciphers | Caesar, ROT13, Atbash, Vigenère, Morse, Braille, NATO spelling |
| text.fancy | mock case, l33t, uwu, Pig Latin, zalgo, upside-down, 𝐛𝐨𝐥𝐝/𝒮𝒸𝓇𝒾𝓅𝓉/wide |
| text.placeholder | lorem ipsum placeholder text, random team and person names |
| data | sorting and searching any collection; lists, maps, tables, charts below |
| data.lists | ranges, map/filter/reduce, building lists |
| data.maps | keys, values, lookups, merging and transforming maps |
| data.sets | unique values in insertion order: union, intersection, difference |
| data.tables | rows under named columns: filter, pick columns, sort |
| data.charts | sparklines, bar charts, histograms, plots, heatmaps, progress bars and trees |
| math | rounding, roots, logs, interpolation, constants; exact fractions and big ints |
| math.stats | sums, averages, spread and extremes of lists; units welcome |
| math.trig | sine, cosine and friends; angles as units |
| math.numtheory | gcd, primes, factoring, factorials, modular powers; exact at any size |
| math.bits | count, test, set, rotate and swap bits |
| math.bases | hex, binary, octal and any base 2-36; digits of a number |
| math.formatting | printf and format specs, commas, numbers in words, ordinals, Roman numerals |
| math.random | random numbers and picks, dice, odds in plain words, fortunes, a magic 8-ball |
| math.uncertainty | measurements with an error, 5 ± 0.1 m, carried through arithmetic |
| math.complex | complex numbers, 2 + 3i; a number touching i is imaginary |
| math.linalg | dot and cross products, matrix multiply, determinant, inverse, linear systems |
| math.calculus | numeric roots, derivatives and integrals of functions |
| units | numbers with units, combined and converted; live currency rates |
| units.constants | physical constants (CODATA 2018); Planck’s constant is h_planck |
| units.money | currencies, live exchange rates, paid time like workday and workyr |
| units.goofy | bananas for scale, smoots, fortnights; every item also works as item_for_scale |
| units.kitchen | cups to grams with ingredient densities |
| finance | pay, interest and growth, loans, splitting bills, margins |
| time | dates, durations, date math, relative text |
| time.calendars | periods, weekdays, business days, week numbers, month grids, cron, zodiacs |
| time.zones | the same moment in UTC, local time, any IANA zone or a city; world clocks |
| time.sky | sunrise, sunset, day length and moon phase for a place and date |
| geo | countries and cities: lookups, distance, bearing, nearest city |
| dev | JWTs, URLs, semver, file permissions; networks, IDs, colors and codes below |
| dev.net | IP addresses, CIDR blocks, subnets, well-known ports |
| dev.ids | UUIDs, ULIDs and nano IDs; secure passwords and passphrases |
| dev.colors | colors as “#rrggbb”: RGB/HSL, mixing, lightening, WCAG contrast |
| dev.codes | check digits for cards, ISBNs and IBANs; HTTP status and MIME type lookups |
| art | clip art, gradients, QR codes, images, animation; frames, fractals, games below |
| art.frames | boxes, cowsay; art side by side, stacked, overlaid, mirrored, rotated, scaled |
| art.generative | L-systems, fractals, the Mandelbrot set, cellular automata, mazes, starfields |
| art.games | chess boards from FEN, playing cards, dice faces |
| art.figlet | big-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,todayor 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"]]
a cookie recipe for 24, scaled to 60 and weighed instead of scooped
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,todayor 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
| function | description |
|---|---|
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(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
type
type(v: any): the type name of a value
type(5 km)
# → "quantity"
type("hi")
# → "str"
type([1])
# → "list"
str
str(v: any): convert to a string (full float precision)
str(1/3)
# → "0.3333333333333333"
str(5 km)
# → "5 km"
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
float
float(v: num|quantity|str): convert to a float; drops a quantity’s unit
float("2.5")
# → 2.5
float(5 km)
# → 5
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]]
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
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,todayor 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
| function | description |
|---|---|
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
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")
mv
mv(from: str, to: str): move or rename a file or directory, creating missing folders
mv("draft.txt", "final.txt")
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"
basename
basename(path: str): the last piece of a path, or nil
"a/b/c.txt".basename
# → "c.txt"
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"
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()
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]}
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]]
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,todayor 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
| function | description |
|---|---|
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,todayor 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
| module | about |
|---|---|
| layout | pad, center, wrap, truncate, dedent; snake_case, camelCase, slugs; words |
| compare | edit distance, similarity, the closest of a list, line-by-line diffs |
| translation | translate text between languages online; cached translations work offline |
| encoding | base64, base32, base58, URL and hex encodings, UTF-8 bytes, hex dumps |
| hash | SHA-1/256/512, MD5, HMAC and CRC-32 of strings |
| ciphers | Caesar, ROT13, Atbash, Vigenère, Morse, Braille, NATO spelling |
| fancy | mock case, l33t, uwu, Pig Latin, zalgo, upside-down, 𝐛𝐨𝐥𝐝/𝒮𝒸𝓇𝒾𝓅𝓉/wide |
| placeholder | lorem ipsum placeholder text, random team and person names |
Functions
| function | description |
|---|---|
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"]
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"
starts_with
starts_with(s: str, prefix: str): whether s starts with prefix
"abc".starts_with("a")
# → true
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"
find_all
find_all(s: str, pat: str|regex): list of all matches
"a1b22c333".find_all(r"\d+")
# → ["1", "22", "333"]
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]
strip_ansi
strip_ansi(s: str): remove terminal color and cursor codes
gradient("ab", "red", "blue").strip_ansi
# → "ab"
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,todayor 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
| function | description |
|---|---|
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..."
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"
center
center(s: any, width: int, fill?: str): center in width characters; extra fill goes right
"hi".center(8, "*")
# → "***hi***"
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
dedent
dedent(s: str): remove the indentation every non-blank line shares
" a\n b\n c".dedent
# → a
# → b
# → c
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"
camel
camel(s: str): camelCase
"user_id_v2".camel
# → "userIdV2"
kebab
kebab(s: str): kebab-case
"MyComponentName".kebab
# → "my-component-name"
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,todayor 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
| function | description |
|---|---|
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,todayor 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
| function | description |
|---|---|
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,todayor 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
| function | description |
|---|---|
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="
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
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"
byte_len
byte_len(v: str|list): length in UTF-8 bytes rather than characters
"héllo".byte_len
# → 6
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"
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,todayor 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
| function | description |
|---|---|
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"
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
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,todayor 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
| function | description |
|---|---|
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"
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"
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,todayor 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
| function | description |
|---|---|
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"
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ǝɥ"
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,todayor 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
| function | description |
|---|---|
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,todayor 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
| module | about |
|---|---|
| lists | ranges, map/filter/reduce, building lists |
| maps | keys, values, lookups, merging and transforming maps |
| sets | unique values in insertion order: union, intersection, difference |
| tables | rows under named columns: filter, pick columns, sort |
| charts | sparklines, bar charts, histograms, plots, heatmaps, progress bars and trees |
Functions
| function | description |
|---|---|
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
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"]
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]
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"
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"
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
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
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,todayor 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
| function | description |
|---|---|
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]
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]]
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]]
unshift
unshift(xs: list, v: any): insert v at the front in place and return the list
[2, 3].unshift(1)
# → [1, 2, 3]
map
map(xs: list, f: fn): apply f to every item
[1, 2, 3].map(|x| x * 10)
# → [10, 20, 30]
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
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}
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]]
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,todayor 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
| function | description |
|---|---|
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
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"
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}
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,todayor 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
| function | description |
|---|---|
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")
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)
remove
remove(s: set, v: any): remove v in place (if there) and return the set
set(1, 2).remove(1)
# → set(2)
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,todayor 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
| function | description |
|---|---|
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}])
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}])
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,todayor 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
| function | description |
|---|---|
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
# → "▅▆▇▇███▇▆▅▄▃▂▁▁▁▂▂▄▅"
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
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
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
# → ░░░░
# → ▒▒▒▒▓▓
# → ▓▓████
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,todayor 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
| module | about |
|---|---|
| stats | sums, averages, spread and extremes of lists; units welcome |
| trig | sine, cosine and friends; angles as units |
| numtheory | gcd, primes, factoring, factorials, modular powers; exact at any size |
| bits | count, test, set, rotate and swap bits |
| bases | hex, binary, octal and any base 2-36; digits of a number |
| formatting | printf and format specs, commas, numbers in words, ordinals, Roman numerals |
| random | random numbers and picks, dice, odds in plain words, fortunes, a magic 8-ball |
| uncertainty | measurements with an error, 5 ± 0.1 m, carried through arithmetic |
| complex | complex numbers, 2 + 3i; a number touching i is imaginary |
| linalg | dot and cross products, matrix multiply, determinant, inverse, linear systems |
| calculus | numeric roots, derivatives and integrals of functions |
Functions
| function | description |
|---|---|
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
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
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
floor
floor(x: num|quantity): round down
floor(2.7)
# → 2
ceil
ceil(x: num|quantity): round up
ceil(2.1)
# → 3
trunc
trunc(x: num|quantity): round toward zero; keeps units
trunc(-2.7)
# → -2
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
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
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
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
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,todayor 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
| function | description |
|---|---|
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
avg
avg(xs: list): mean of a list
[1, 2, 4].avg
# → 2.33333
[2 h, 30 min].avg
# → 1.25 h
product
product(xs: list): multiply a list together
[2, 3, 4].product
# → 24
[2 m, 3 m].product
# → 6 m^2
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"
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
min
min(xs: list) / min(a: any, b: any, ...): smallest value
min(3, 9, 4)
# → 3
[2 km, 1 mi].min
# → 1 mi
max
max(xs: list) / max(a: any, b: any, ...): largest value
max(3, 9, 4)
# → 9
["b", "a"].max
# → "b"
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]
normalize
normalize(xs: list): rescale so the min is 0 and the max is 1
[10, 15, 20].normalize
# → [0, 0.5, 1]
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]
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]
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,todayor 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
| function | description |
|---|---|
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
cos
cos(x: num|quantity): cosine of radians or an angle unit
cos(60 deg)
# → 0.5
tan
tan(x: num|quantity): tangent of radians or an angle unit
tan(45 deg)
# → 1
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
atan2
atan2(y: num|quantity, x: num|quantity): angle of the point (x, y), as an angle
atan2(1, -1) to deg
# → 135 deg
sinh
sinh(x: num): hyperbolic sine
sinh(1)
# → 1.1752
cosh
cosh(x: num): hyperbolic cosine
cosh(1)
# → 1.54308
tanh
tanh(x: num): hyperbolic tangent
tanh(1)
# → 0.761594
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,todayor 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
| function | description |
|---|---|
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]
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
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,todayor 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
| function | description |
|---|---|
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
clear_bit
clear_bit(n: int, i: int): n with bit i cleared
clear_bit(0xff, 0)
# → 0xfe
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,todayor 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
| function | description |
|---|---|
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"
bin
bin(v: int|float, bits?: int): same as v to bin / v to bin(bits)
bin(10)
# → 0b1010
bin(5, 8)
# → 0b00000101
oct
oct(v: int|float, bits?: int): same as v to oct
oct(8)
# → 0o10
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
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
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,todayor 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
| function | description |
|---|---|
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"
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"
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,todayor 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
| function | description |
|---|---|
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
choice
choice(xs: list): random item
["rock", "paper", "scissors"].choice
# → "scissors"
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
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
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."
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,todayor 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
| function | description |
|---|---|
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,todayor 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
| function | description |
|---|---|
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
arg
arg(z: num|complex): angle from the positive real axis, as an angle
arg(1i) to deg
# → 90 deg
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)"
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,todayor 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
| function | description |
|---|---|
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
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
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]]
det
det(m: list): determinant of a square matrix
det([[1, 2], [3, 4]])
# → -2
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]]
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]
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,todayor 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
| function | description |
|---|---|
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
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,todayor 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
| kind | names |
|---|---|
| length | m, 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 |
| mass | kg, 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 |
| time | s, 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 |
| temperature | K, kelvin; C, celsius, degC; F, fahrenheit, degF |
| volume | L, 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 |
| area | ha, hectare, hectares; acre, acres |
| speed | kph, kmh; mph; kn, knot, knots |
| data | bit, bits; B, byte, bytes; KB; MB; GB; TB; PB; KiB; MiB; GiB; TiB; PiB; kbit, Kb; Mbit, Mb; Gbit, Gb |
| rate | bps; kbps; Mbps; Gbps |
| energy | J, joule, joules; kJ; MJ; cal, calorie, calories; kcal, Cal; Wh; kWh; eV; BTU, btu |
| power | W, watt, watts; kW; MW; hp, horsepower |
| pressure | Pa, pascal; kPa; MPa; bar; atm; psi; mmHg |
| force | N, newton, newtons; kN; lbf |
| angle | rad, radian, radians; deg, degree, degrees; turn, turns |
| current | A, amp, amps, ampere, amperes; mA, milliamp, milliamps; µA, uA, μA; kA |
| voltage | V, volt, volts; mV; µV, uV, μV; kV |
| resistance | Ω, ohm, ohms; mΩ, mohm; kΩ, kohm; MΩ, Mohm |
| charge | coulomb, coulombs; mAh; Ah |
| capacitance | farad, farads; mF; µF, uF, μF; nF; pF |
| conductance | S, siemens; mS |
| magnetic_flux | Wb, weber, webers; mWb |
| magnetic_field | T, tesla, teslas; mT; µT, uT, μT |
| inductance | H, henry, henries; mH; µH, uH, μH |
| frequency | Hz, hertz; kHz; MHz; GHz; rpm |
| amount | mol, mole, moles; mmol; µmol, umol, μmol; kmol |
| concentration | M, molar; mM, millimolar; µM, uM, μM, micromolar |
| light | lm, lumen, lumens; cd, candela, candelas |
| illuminance | lx, lux; fc, footcandle, footcandles |
| dose | Sv, sievert, sieverts; mSv, millisievert, millisieverts; µSv, uSv, μSv, microsievert, microsieverts |
Submodules
| module | about |
|---|---|
| constants | physical constants (CODATA 2018); Planck’s constant is h_planck |
| money | currencies, live exchange rates, paid time like workday and workyr |
| goofy | bananas for scale, smoots, fortnights; every item also works as item_for_scale |
| kitchen | cups to grams with ingredient densities |
Functions
| function | description |
|---|---|
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
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,todayor 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,todayor 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
paid time
# 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,todayor 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
| kind | names |
|---|---|
| length | banana, 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 |
| mass | grain_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 |
| time | planck_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 |
| volume | shot, shots; can, cans; wine_bottle, wine_bottles; barrel, barrels, bbl; hogshead, hogsheads; bathtub, bathtubs; olympic_pool, olympic_pools |
| area | barn, barns; parking_space, parking_spaces; tennis_court, tennis_courts; rhode_island; wales |
| speed | snail, snails; walking_pace; bike_pace; mach; lightspeed |
| data | nibble, 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 |
| energy | aa_battery, aa_batteries; banana_cal; big_mac, big_macs; gallon_gas, gallons_gas; tnt, ton_tnt; hiroshima, hiroshimas |
| power | human_resting; donkeypower; toaster, toasters |
| frequency | heartbeat, heartbeats; hummingbird_wingbeat, hummingbird_wingbeats |
| dose | bed, banana_dose |
Functions
| function | description |
|---|---|
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,todayor 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
| function | description |
|---|---|
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,todayor 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
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
cagr
cagr(start: num|quantity, end: num|quantity, time: quantity): compound annual growth rate
cagr($1000, $2500, 8 yr)
# → 12.1353%/yr
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
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%"
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,todayor 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
| module | about |
|---|---|
| calendars | periods, weekdays, business days, week numbers, month grids, cron, zodiacs |
| zones | the same moment in UTC, local time, any IANA zone or a city; world clocks |
| sky | sunrise, sunset, day length and moon phase for a place and date |
Functions
| function | description |
|---|---|
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
month
month(d: date): month of a date (1-12)
now.month
# → 10
day
day(d: date): day of the month
now.day
# → 8
hour
hour(d: date): hour of a date
now.hour
# → 7
minute
minute(d: date): minute of a date
now.minute
# → 58
second
second(d: date): second of a date
now.second
# → 47
weekday
weekday(d: date): day name
date("2026-12-25").weekday
# → "Friday"
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%"
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
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
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"
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"
unix
unix(d: date): seconds since 1970-01-01 UTC; same as d to unix
date(0).unix
# → 0
date(86400) to unix
# → 86400
unix_ms
unix_ms(d: date): milliseconds since 1970-01-01 UTC; same as d to unix_ms
date(1).unix_ms
# → 1000
date_ms
date_ms(ms: int): the date from Unix milliseconds, as JavaScript and Java give them
date_ms(1798178400000) == date(1798178400)
# → true
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,todayor 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
| function | description |
|---|---|
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
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,todayor 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
| function | description |
|---|---|
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
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
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,todayor 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
| function | description |
|---|---|
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
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,todayor 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
| function | description |
|---|---|
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"
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"]
cities
cities(): every city as a table, biggest first
cities()[0].name
# → "Shanghai"
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
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,todayor 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
| module | about |
|---|---|
| net | IP addresses, CIDR blocks, subnets, well-known ports |
| ids | UUIDs, ULIDs and nano IDs; secure passwords and passphrases |
| colors | colors as “#rrggbb”: RGB/HSL, mixing, lightening, WCAG contrast |
| codes | check digits for cards, ISBNs and IBANs; HTTP status and MIME type lookups |
Functions
| function | description |
|---|---|
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
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"}
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,todayor 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
| function | description |
|---|---|
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
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}
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,todayor 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
| function | description |
|---|---|
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"
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"
nanoid
nanoid(len?: int): a URL-safe random ID (default 21 characters, like a UUID’s strength)
nanoid()
# → "dcecD0JN2UQg7bZKAnT-Z"
nanoid(8)
# → "HDMzFW6a"
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,todayor 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
| function | description |
|---|---|
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"
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]
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]
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"
lighten
lighten(c: str|list, amount: num): raise HSL lightness by amount (0-1)
lighten("#3478f6", 20%)
# → "#96b9fa"
darken
darken(c: str|list, amount: num): lower HSL lightness by amount (0-1)
darken("#3478f6", 20%)
# → "#0847bc"
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,todayor 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
| function | description |
|---|---|
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
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,todayor 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
| module | about |
|---|---|
| frames | boxes, cowsay; art side by side, stacked, overlaid, mirrored, rotated, scaled |
| generative | L-systems, fractals, the Mandelbrot set, cellular automata, mazes, starfields |
| games | chess boards from FEN, playing cards, dice faces |
| figlet | big-letter banners in FIGlet fonts like slant and shadow, or any .flf file |
Functions
| function | description |
|---|---|
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
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
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")
# → ▄▄ ▄▄ ▄▄ ▄▄ ▄▄ ▄▄
# → █ █ █ █▄▄ █▄▄ █▄▄ █▄▄
# → █▄▄ █▄▄█ █ █ █▄▄ █▄▄
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
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)
More examples
gallery
# 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,todayor 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
| function | description |
|---|---|
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 │
# → ╰──────╯
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)
# → /-------\/
# → / | ||
# → * ||----||
# → ~~ ~~
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
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")
# → /\
# → \/
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,todayor 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
| function | description |
|---|---|
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"]
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)
# → ▄▀▄
# → ▄▀▄ ▄▀▄
# → ▄▀▄ ▄▀▄
# → ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄
# → ▄▀▄ ▄▀▄
# → ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄
# → ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄
# → ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄ ▄▀▄
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,todayor 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
| function | description |
|---|---|
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│
# → └─────┘
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])
# → ┌───────┐ ┌───────┐ ┌───────┐
# → │ │ │ ● │ │ ● │
# → │ ● │ │ │ │ ● │
# → │ │ │ ● │ │ ● │
# → └───────┘ └───────┘ └───────┘
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,todayor 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
| function | description |
|---|---|
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
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 // / │
# → │ \_//___| │
# → ╰───────────╯