
Created 2025-09-13
//โ dog-|๐ฉ๐ฉ๐ฉ๐ฉ๐ฉ
//โ cat-|๐ฉ๐ฉ๐ฉ๐ฉ
//โ mouse-|๐ฉ๐ฉ๐ฉ
//โ playing-|๐ฉ
//โ yard-|๐ฉ
//โ barked-|๐ฉ
//โ loudly-|๐ฉ
//โ ran-|๐ฉ
//โ quickly-|๐ฉ
//โ hid-|๐ฉ
//โ under-|๐ฉ
//โ bench-|๐ฉ
//โ kept-|๐ฉ
//โ looking-|๐ฉ
//โ jumped-|๐ฉ
//โ small-|๐ฉ
//โ fence-|๐ฉ
//โ followed-|๐ฉ
//โ bird-|๐ฉ
//โ watched-|๐ฉ
//โ silently-|๐ฉ
//โ moved-|๐ฉ
echo(rows.join("\n"));
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* References
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Here are the links to the JavaScript APIs we used:
*
* - `string.split(splitter)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split
* - `array.filter(callback)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
* - `array.includes(value)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
* - `array.reduce(callback, initialValue)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
* - `Object.keys(object)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
* - `Object.entries(object)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
* - `Math.max(...array)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
* - `array.map(callback)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
* - `string.padStart(length, padString)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
* the padString. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
* - `string.repeat(value)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
* - `array.join(separator)`: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join
*/

Created 2025-09-12
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
//โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
{
let output = "";
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
const [x, y] = [pos(j), pos(i)];
const r = Math.hypot(x, y);
const theta = Math.atan2(y, x) / (Math.PI * 2);
const phase = now / 3000;
const l = ((r + theta - phase) % 1) * -360;
const index = ~~rotate(l);
output += moons[index];
}
output += i == size - 1 ? "" : "\n";
}
echo(output);
}
const d3 = recho.require("d3");
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* References
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* [1] https://observablehq.com/d/a741f9a27e8c0e73
*/

Created 2025-09-12
//โ โ โ โโโโปโโโโโโโปโโโโโโโปโโโโโโโปโโโโโโโปโโโโโ โ
//โ โ January โ ใใใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใใใ โ
//โ โ February โ ใใใใใใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใใใ โ
//โ โ March โ ใใใใใใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ โ
//โ โ April โ ใใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใใใใใ โ
//โ โ May โ ใใใใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใใ โ
//โ โ June โ ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใใใใใใใ โ
//โ โ July โ ใใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใใใใ โ
//โ โ August โ ใใใใใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใ โ
//โ โ September โ ใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใใใใใใ โ
//โ โ October โ ใใใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใใใ โ
//โ โ November โ ใใใใใใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใ โ
//โ โ December โ ใ๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐ใใใใใ โ
{
const matrix = _.times(12, () => _.times(6 * 7, () => SPACE));
for (const day of days) {
const row = day.getMonth();
const column = day.getDate() + months[row].getDay();
matrix[row][column] = getMoonEmoji(day);
}
// Remove the leading and trailing spaces from the header and each row.
const head = Math.min(...matrix.map((xs) => xs.findIndex((x) => x !== SPACE)));
const tail = Math.max(...matrix.map((xs) => xs.findLastIndex((x) => x !== SPACE))) + 1;
echo(line(" ".repeat(longestLength), header.slice(head, tail)), {quote: false});
echo(matrix.map((x, i) => line(alignedMonthNames[i], x.slice(head, tail).join(""))).join("\n"));
}
function line(...items) {
return "โ " + items.join(" โ ") + " โ";
}
const header = "โโโโโปโโ".repeat(7);
const months = d3.timeMonths(theFirstDay, theLastDay);
const longestLength = monthNames.reduce((x, y) => Math.max(x, y.length), 0);
const monthNames = months.map(d3.timeFormat("%B"));
const alignedMonthNames = monthNames.map((n) => n.padStart(longestLength, " "));
const theFirstDay = d3.timeYear(new Date(year, 0, 1));
const theLastDay = d3.timeYear.offset(theFirstDay, 1);
const days = d3.timeDays(theFirstDay, theLastDay);
const SPACE = "\u3000"; // Full-width space
function getMoonEmoji(date) {
const index = Math.round(suncalc.getMoonIllumination(date).phase * 8);
return String.fromCodePoint(0x1f311 + (index === 8 ? 0 : index));
}
const suncalc = recho.require("suncalc");
const d3 = recho.require("d3");
const _ = recho.require("lodash");

Created 2025-09-11
//โ โโโฌโโโโโโโโฌโโโโโโฌโโโโโโโโโโโโโโโโโโโโ
//โ โ โ โ โ
//โ โ โ โโโฌโโ โ โโโ โโโโโโโ โโโโโโโ โโโ โ
//โ โ โ โ โ โ โ โ โ โ โ
//โ โ โ โ โ โโโโโค โโโค โโโ โโโ โโโโโโโค โโโค
//โ โ โ โ โ โ โ โ โ โ โ โ
//โ โ โโโ โ โ โโโ โ โ โ โโโโโฌโโ โโโโโดโโ โ
//โ โ โ โ โ โ โ โ โ โ โ
//โ โโโโโโโค โ โ โโโ โโโค โ โ โ โ โ โโโฌโโโโค
//โ โ โ โ โ โ โ โ โโ โ โ โ โ โ
//โ โ โโโโโ โ โ โ โโโ โโโโโ โ โ โโโ โโโ โ
//โ โ โ โ โ โ โ โ โ โ โ
//โ โ โ โโโฌโโดโโโโดโโโโ โ โโโโโดโโค โโโค โ โโโค
//โ โ โ โ โ โ โ โ โ โ
//โ โ โโโ โโโ โโโฌโโ โโโผโโโโโโ โ โ โ โโโ โ
//โ โ โ โ โ โ โ โ โ โ โ
//โ โ โโโดโโ โโโ โ โโโ โ โโโโโฌโโดโโ โโโฌโโ โ
//โ โ โ โ โ โ โ โ โ โ
//โ โ โโโ โ โ โโโ โโโดโโโโ โโโ โ โโโโโ โโโ
//โ โ โ โ โ
//โ โโโโโดโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโ
{
frame;
if (!maze.walk()) maze.regenerate();
echo(maze.render());
}
const width = 18;
const height = 10;
const maze = new Maze(width, height);
const frame = recho.interval(100);
const cell = () => ({top: true, right: true, bottom: true, left: true});
class Maze {
constructor(width, height) {
this.width = width;
this.height = height;
this.initialize();
this.generate();
}
initialize() {
this.grid = _.times(this.height, () => _.times(this.width, cell));
this.visited = _.times(this.height, () => _.times(this.width, _.constant(false)));
this.path = null;
this.step = 0;
}
regenerate() {
this.initialize();
this.generate();
}
generate() {
this.carvePassage(0, 0);
this.grid[0][0].left = false;
this.grid[this.height - 1][this.width - 1].right = false;
}
walk() {
if (this.path === null) {
this.path = this.findPath();
this.step = 0;
return true;
} else if (this.step + 1 < this.path.length) {
this.step += 1;
return true;
} else {
return false;
}
}
carvePassage(x, y) {
this.visited[y][x] = true;
for (const neighbor of _.shuffle(this.getUnvisitedNeighbors(x, y))) {
if (!this.visited[neighbor.y][neighbor.x]) {
// Remove walls between current cell and neighbor
this.removeWall(x, y, neighbor.x, neighbor.y);
// Recursively visit neighbor
this.carvePassage(neighbor.x, neighbor.y);
}
}
}
getUnvisitedNeighbors(x, y) {
const neighbors = [];
if (y > 0 && !this.visited[y - 1][x]) neighbors.push({x: x, y: y - 1}); // up
if (x < this.width - 1 && !this.visited[y][x + 1]) neighbors.push({x: x + 1, y: y}); // right
if (y < this.height - 1 && !this.visited[y + 1][x]) neighbors.push({x: x, y: y + 1}); // down
if (x > 0 && !this.visited[y][x - 1]) neighbors.push({x: x - 1, y: y}); // left
return neighbors;
}
removeWall(x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
if (dx === 1) {
// Moving right
this.grid[y1][x1].right = false;
this.grid[y2][x2].left = false;
} else if (dx === -1) {
// Moving left
this.grid[y1][x1].left = false;
this.grid[y2][x2].right = false;
} else if (dy === 1) {
// Moving down
this.grid[y1][x1].bottom = false;
this.grid[y2][x2].top = false;
} else if (dy === -1) {
// Moving up
this.grid[y1][x1].top = false;
this.grid[y2][x2].bottom = false;
}
}
findPath() {
const queue = [[0, 0, [{x: 0, y: 0}]]];
const visited = new Set([`${0},${0}`]);
while (queue.length > 0) {
const [x, y, path] = queue.shift();
if (x === this.width - 1 && y === this.height - 1) return path;
for (const {dx, dy, direction} of directions) {
const nx = x + dx;
const ny = y + dy;
const key = `${nx},${ny}`;
if (
nx >= 0 &&
nx < this.width &&
ny >= 0 &&
ny < this.height &&
!visited.has(key) &&
!this.grid[y][x][direction]
) {
visited.add(key);
queue.push([nx, ny, [...path, {x: nx, y: ny}]]);
}
}
}
return null;
}
render() {
const pathSet = new Set();
if (this.path !== null && this.step < this.path.length) {
pathSet.add(`${this.path[this.step].x},${this.path[this.step].y}`);
}
// Create output grid (2x dimensions + 1 for walls)
const outputHeight = this.height * 2 + 1;
const outputWidth = this.width * 2 + 1;
const output = _.times(outputHeight, () => _.times(outputWidth, _.constant(chars.space)));
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
const cell = this.grid[y][x];
const ox = x * 2;
const oy = y * 2;
if (cell.top) output[oy][ox + 1] = chars.horizontal;
if (cell.bottom) output[oy + 2][ox + 1] = chars.horizontal;
if (cell.left) output[oy + 1][ox] = chars.vertical;
if (cell.right) output[oy + 1][ox + 2] = chars.vertical;
if (pathSet.has(`${x},${y}`)) output[oy + 1][ox + 1] = chars.ghost;
}
}
// Draw corners and junctions
for (let y = 0; y < outputHeight; y += 2) {
for (let x = 0; x < outputWidth; x += 2) {
const top = y > 0 && output[y - 1][x] === chars.vertical;
const bottom = y < outputHeight - 1 && output[y + 1][x] === chars.vertical;
const left = x > 0 && output[y][x - 1] === chars.horizontal;
const right = x < outputWidth - 1 && output[y][x + 1] === chars.horizontal;
const connections = (top ? 1 : 0) + (right ? 1 : 0) + (bottom ? 1 : 0) + (left ? 1 : 0);
if (connections === 0) {
output[y][x] = chars.space;
} else if (connections === 1) {
if (top || bottom) output[y][x] = chars.vertical;
else output[y][x] = chars.horizontal;
} else if (connections === 2) {
if (top && bottom) output[y][x] = chars.vertical;
else if (left && right) output[y][x] = chars.horizontal;
else if (top && right) output[y][x] = chars.bottomLeft;
else if (top && left) output[y][x] = chars.bottomRight;
else if (bottom && right) output[y][x] = chars.topLeft;
else if (bottom && left) output[y][x] = chars.topRight;
} else if (connections === 3) {
if (!top) output[y][x] = chars.teeDown;
else if (!right) output[y][x] = chars.teeLeft;
else if (!bottom) output[y][x] = chars.teeUp;
else if (!left) output[y][x] = chars.teeRight;
} else {
output[y][x] = chars.cross;
}
}
}
output[1][0] = chars.startMarker;
output[outputHeight - 2][outputWidth - 1] = chars.endMarker;
return output.map((row) => row.join("")).join("\n");
}
}
const directions = [
{dx: 0, dy: -1, direction: "top"},
{dx: 1, dy: 0, direction: "right"},
{dx: 0, dy: 1, direction: "bottom"},
{dx: -1, dy: 0, direction: "left"},
];
const chars = {
horizontal: "โ",
vertical: "โ",
topLeft: "โ",
topRight: "โ",
bottomLeft: "โ",
bottomRight: "โ",
teeUp: "โด",
teeDown: "โฌ",
teeLeft: "โค",
teeRight: "โ",
cross: "โผ",
space: " ",
ghost: "โ",
startMarker: " ",
endMarker: " ",
};
const _ = recho.require("lodash");

Created 2025-09-09
//โ ๐๐จ
//โ ๐ข๐จ
//โ ๐ถโโ๏ธ๐จ
//โ ๐๐จ
//โ ๐๐จ
{
const x = (count) => 40 - (count % 40);
echo("๐๐จ".padStart(x(snail)), {quote: false});
echo("๐ข๐จ".padStart(x(turtle)), {quote: false});
echo("๐ถโโ๏ธ๐จ".padStart(x(human)), {quote: false});
echo("๐๐จ".padStart(x(car)), {quote: false});
echo("๐๐จ".padStart(x(rocket)), {quote: false});
}

Created 2025-09-03
//โ
//โ Great Britain
//โ pigs -| ๐
//โ cattle -| ๐ ๐ ๐ ๐
//โ sheep -| ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ
//โ United States
//โ pigs -| ๐ ๐ ๐ ๐ ๐ ๐
//โ cattle -| ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ sheep -| ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ
//โ Live stock (millions)
//โ
echo(output);
/**
* Next, let's dive into how `output` is generated.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Preparing the data
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* First, we need to prepare the data. We're going to use the following dataset
* to create the chart. It's a tiny tubular dataset, with each row representing
* a animal in a country and the count of the animal.
*/
const data = [
{animal: "pigs", country: "Great Britain", count: 1354979},
{animal: "cattle", country: "Great Britain", count: 3962921},
{animal: "sheep", country: "Great Britain", count: 10931215},
{animal: "pigs", country: "United States", count: 6281935},
{animal: "cattle", country: "United States", count: 9917873},
{animal: "sheep", country: "United States", count: 7084151},
];
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Importing D3
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Then we import D3 to help us with the data processing. In Recho, you can
* typically use `recho.require(name)` to import an external library.
*
* > Ref. https://recho.dev/docs/libraries-imports
* > Ref. https://d3js.org/
*/
const d3 = recho.require("d3");
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Generating the bars
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* We'll get started with generating the bars. There are three main tasks here:
*
* 1. Mapping animals types to their corresponding emojis.
* 2. Mapping the counts to the number of emojis.
* 3. Generating the bars based on the emojis and the number.
*
* Here is the implementation:
*/
//โ [ 0, 1, 2, 3, 4, 5 ]
const I = echo(d3.range(data.length));
//โ { cattle: "๐", sheep: "๐", pigs: "๐" }
const emoji = echo({cattle: "๐", sheep: "๐", pigs: "๐"});
//โ [ "๐", "๐", "๐", "๐", "๐", "๐" ]
const E = echo(data.map((d) => emoji[d.animal]));
//โ [ 1, 4, 11, 6, 10, 7 ]
const V = echo(data.map((d) => Math.round(d.count / 1e6)));
//โ [ "๐ ", "๐ ๐ ๐ ๐ ", "๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ", "๐ ๐ ๐ ๐ ๐ ๐ ", "๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ", "๐ ๐ ๐ ๐ ๐ ๐ ๐ " ]
const bars = echo(I.map((i) => `${E[i]} `.repeat(V[i])));
/** This is the chart we got so far. */
//โ ๐
//โ ๐ ๐ ๐ ๐
//โ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ ๐ ๐ ๐ ๐ ๐ ๐
//โ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ ๐ ๐ ๐ ๐ ๐ ๐ ๐
echo(bars.join("\n"));
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Adding the labels
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Next step is to add the labels to the bars. We need to collect all the
* animal types by a set, and compute a margin left to make sure the labels
* are aligned. Then concatenate the labels to the bars with a separator: `-|`.
*/
//โ [ "pigs", "cattle", "sheep" ]
const L = echo(Array.from(new Set(data.map((d) => d.animal))));
//โ 6
const marginLeft = echo(d3.max(L, (d) => d.length));
//โ [ " pigs", "cattle", " sheep", " pigs", "cattle", " sheep" ]
const labels = echo(data.map((d) => d.animal.padStart(marginLeft, " ")));
//โ [ " pigs -| ๐ ", " cattle -| ๐ ๐ ๐ ๐ ", " sheep -| ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ", " pigs -| ๐ ๐ ๐ ๐ ๐ ๐ ", " cattle -| ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ", " sheep -| ๐ ๐ ๐ โฆ
const rows = echo(I.map((i) => " " + labels[i] + " -| " + bars[i]));
/** Now the chart looks like this. */
//โ pigs -| ๐
//โ cattle -| ๐ ๐ ๐ ๐
//โ sheep -| ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ pigs -| ๐ ๐ ๐ ๐ ๐ ๐
//โ cattle -| ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ sheep -| ๐ ๐ ๐ ๐ ๐ ๐ ๐
echo(rows.join("\n"));
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Generating the titles
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Technically speaking, the chart is a facet chart, which means it contains
* multiple charts. The first one is for Great Britain, and the second one is
* for United States. In order to differentiate the two charts, we need to add
* the titles and some spacing.
*/
//โ 45
const width = echo(d3.max(rows, (d) => d.length));
//โ [ "Great Britain", "United States" ]
const T = echo(Array.from(new Set(data.map((d) => d.country))));
//โ [ " Great Britain", " United States" ]
const titles = echo(T.map((t) => t.padStart(Math.ceil(width / 2 + 2), " ")));
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Final output
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Finally, we can concatenate the titles, the rows, and the live stock caption
* to get the final output!
*/
//โ
//โ Great Britain
//โ pigs -| ๐
//โ cattle -| ๐ ๐ ๐ ๐
//โ sheep -| ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ
//โ United States
//โ pigs -| ๐ ๐ ๐ ๐ ๐ ๐
//โ cattle -| ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ sheep -| ๐ ๐ ๐ ๐ ๐ ๐ ๐
//โ
//โ Live stock (millions)
//โ
const output = echo(
[
" ",
titles[0], // Great Britain
...rows.slice(0, 3),
" ",
titles[1], // United States
...rows.slice(3),
" ",
"Live stock (millions)", // Add a caption
" ",
].join("\n"),
);

Created 2025-09-03
//โ ใใใใใใใใใใใใใใใใใใใใใใ็
//โ ็็ใใใใใใใใ็็ใใ็ใใใใใ็็ใใ็
//โ ใใ็็ใใใใใใใ็ใใใใใใใใใใใใ็
//โ ใใใใ็็็ใใ็็ใ็ใใใใ็็็็ใใใ็็ใใ็็ใ็็ฑ็็็
//โ ใใใ็็ฑใใใใ็็็ใ็็ใใใ็็็็็ใใใใ็็ใใ็็็ฑ็็
//โ ็ฑ็ใใใ็ใใใใ็็็็็ใใ็ฑ็ฑ็ฑ็็
//โ ใใใ็ฑ็ฑ็ฑ็็็็็็็ฑ็ฑ็ฑ็็็ใ็ฑ็็ฑใใใใใใ็็ฑ็ฑ
//โ ็็็็็็็ใใใใใ็็็็ฑ็ฑ็
//โ ็ฑ็ฑใใ็ฑ็็็็็็็ฑ็ฑ็็ฑ็ฑ็ฑใใใใ็็ฑ็ฑ็็็็็ฑ็ฑ็ฑ็็็็็ฑ
//โ ็็็็็็ฑ็ฑ็็ฑ็็็็็ใใ็็็็็ฑ็ฑ็็็็็็ฑ็ใ็็ใ็ฑ็ฑ็
//โ ็็็ใ็็็็็็็็็็็็ฑ็ฑ็ฑ็ใ็็ใ็ฑ็ฑ็็็ฑ็็็ฑ็ฑ็ฑ็ฑ็็
//โ ็ฑ็ฑ็ฑ็ฑ็ฑ็ฑ็ฑ็ฑ็็็็็ฑ็ฑ็ฑ็ฑ็ฑ็ซ็ฑ็็็ฑใ็ฑ็็ฑ็็ฑ็็็็็็็ฑ็ฑ
//โ ็ฑ็็็็็ฑ็็็็็ซ็ซ็ฑ็ฑ็ซ็ซ็ซ็ซ็ซ็ซ็ฑ็็็ใ็็็ใใ็ฑ็็็็็ซ
//โ ็็็ฑ็ฑ็็็็ฑ็็ซ็ฑ็็็ฑ็ฑ็็ฑ็็็ฑ็็ซ็ฑ็็ฑ็ฑ็ฑ็็ซ็ฑ็็็ฑ็ฑ็ฑ็
//โ ็ซ็็ฑ็ฑ็ซ็็็ฑ็็ฑ็ซ็็็็็็็็็ฑ็็ฑ็ซ็ฑ็ซ็็ฑ็ฑ็็็ฑ็ฑ็ฑ็ฑ็็ซ
{
frame;
for (let y = 0; y < rows - 1; y++) {
for (let x = 0; x < cols; x++) {
const decay = d3.randomInt(0, 3)();
const spread = d3.randomInt(-1, 1)();
const i = Math.min(Math.max(0, x - spread), cols - 1);
const target = fire[index(i, y + 1)];
fire[index(x, y)] = Math.max(0, target - decay);
}
}
const noise = new Noise();
const linear = d3.scaleLinear([0, 1], [0, rows]);
const source = (i) => linear(noise.get(i, 0, 0));
for (let x = 0; x < cols; x++) {
fire[index(x, rows - 1)] = ~~source(x);
}
const quantile = d3.scaleQuantile(d3.extent(fire), chs);
const ch = (d) => (d ? quantile(d) : "ใ");
let output = "";
for (let i = 0; i < rows; ++i) {
for (let j = 0; j < cols; ++j) output += ch(fire[index(j, i)]);
output += i === rows - 1 ? "" : "\n";
}
output = output.split("\n").map((d) => " " + d).join("\n");
echo(output);
}
const d3 = recho.require("d3");
/**
* I like this example also because I found one importable noise library:
* `perlin-noise-3d`:
*
* - https://www.npmjs.com/package/perlin-noise-3d
*/
const Noise = recho.require("perlin-noise-3d");
/**
* Perlin noise is so useful in creative coding, so it's great that we can use
* it in Recho before making it a built-in function.
*
* As you can see, the implementation replies on noise function to generate
* natural source of fire, which is the key to create such effect!
*/

Created 2025-08-22
//โ w $ R | @ 2 !
//โ j M & C m ,
//โ _# D 9 z L ? Z=
//โ !d ` [ * g g 3
//โ t| [ - BS E k e u< J : o6 j
//โ I* ! j R$ p7 p I&D k, j ?1 M
//โ k j# W O D ( , > a s# XY 9' . XK y )
//โ a $\ *n [B W W F 6[ w> R AOL p j _B U Z
//โ X /1 NX9 K ~ 7 Z /u b_ o H ,:u=$ 6
//โ Btb* rE a yD i n ^ ks , S! |? & ?$ l a
//โ $ 3 @ y ro 9 b Oh ! I- ^7 x =t X
//โ 8 n < F 8ZG - + 8i 2 .j j bx ,tf G
//โ _ 8 t 3 GH& T u S K y M ! Ln 45Y K L
//โ } d ca @ h 4 T 7 6 T ( !y_hx7 P r
//โ u i / FH0 = K v P ` e <[ XjD <6 u
//โ M% V { vg | V 4 q 7 [ R ; [@ =; \
//โ R y [: } ) `v S [ % / g- u= 3
//โ g $ UH g & rD @ b H = o 4+ R
//โ 9 g 96 W c ~ t * mq
//โ ; vI ) p \ M
//โ ) wc A ~
//โ d _ = D
//โ ` ~ q #
//โ | m e
//โ e W ^
{
frame;
// Create a new buffer.
const buffer = d3.range(width * height).map(() => " ");
// Update all columns.
for (let i = columns.length - 1; i >= 0; --i) {
const column = columns[i];
const {lifespan, length, chars} = column;
const n = chars.length;
if (lifespan < 0) columns[i] = createColumn(height);
else if (lifespan <= n) chars[n - lifespan] = " ";
else {
for (let j = length - 1; j < n; ++j) chars[j] = randomChar();
chars.push(randomChar());
}
column.lifespan -= 1;
}
// Update the buffer.
for (let i = 0; i < columns.length; ++i) {
const column = columns[i];
const {y, chars} = column;
for (let j = 0; j < chars.length; ++j) buffer[(y + j) * width + i] = chars[j];
}
// Render the buffer.
let output = "";
for (let i = 0; i < height; ++i) {
for (let j = 0; j < width; ++j) output += buffer[i * width + j];
output += i === height - 1 ? "" : "\n";
}
output = output.split("\n").map((d) => " " + d).join("\n");
echo(output);
}
function createColumn(height) {
const lifespan = d3.randomInt(height)();
const length = d3.randomInt(lifespan)();
const chars = d3.range(length).map(randomChar);
const y = d3.randomInt(0, 10)();
return {lifespan, chars, y};
}
function randomChar() {
return String.fromCharCode(d3.randomInt(32, 127)());
}
const frame = recho.interval(1000 / 15);
const d3 = recho.require("d3");

Created 2025-08-22
//โ โ
//โ โ โ โ โ โโ
//โ โ โ โ โ โ โ โ โโ
//โ โ โ โโ โ โ โโ โ โ โโโ โ โ โโ
//โ โโ โโโโโโ โ โโ โโ โ โโโโ โโ โโโโ โ โโ โโ
//โ โโโโโโโโโโ โโโโโโโโโโโโ โโโโ โโ โโโโโโโโโโ โโโ
//โ โโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโ
//โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
{
let output = "";
for (let i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
const bin = bins[j];
const h = bin ? (bin * height) / d3.max(bins) : 0;
output += h >= height - i ? "โ" : " ";
}
output += i === height - 1 ? "" : "\n";
}
echo(output);
}

Created 2025-08-21
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท*OoO@@@@@@@@@@@@@@@@@@@*ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท@@@@@@@@@@@@@@@@@@@@@@ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท@@@@@@@@@@@@@@@@@@@@@@@@@@ยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท@ยทO@**ยทยทยทยท*@@@@@@@@@@@@@@@@@@@@@@@@Oยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท*@@@@@@@@ยทยท@@@@@@@@@@@@@@@@@@@@@@@@@@ยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท*@@@@@@@@@@*@@@@@@@@@@@@@@@@@@@@@@@@@oยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท*o*@@@@@@@@@@@o@@@@@@@@@@@@@@@@@@@@@@@@oยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยท@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท*o*@@@@@@@@@@@o@@@@@@@@@@@@@@@@@@@@@@@@oยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท*@@@@@@@@@@*@@@@@@@@@@@@@@@@@@@@@@@@@oยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท*@@@@@@@@ยทยท@@@@@@@@@@@@@@@@@@@@@@@@@@ยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท@ยทO@**ยทยทยทยท*@@@@@@@@@@@@@@@@@@@@@@@@Oยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท@@@@@@@@@@@@@@@@@@@@@@@@@@ยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท@@@@@@@@@@@@@@@@@@@@@@ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท*OoO@@@@@@@@@@@@@@@@@@@*ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท@@@@@@@@@@@@@@@@@@@@ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท@@ยท*@@@@@@@@@@@@ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท@ยทยทยทยทยทยทยท@@@*ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทO@@@@*ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทo@@@@*ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทO@ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
//โ ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
{
let output = "";
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const re = map(x, 0, cols, -2.5, 1);
const im = map(y, 0, rows, -1, 1);
let [a, b, i] = [0, 0, 0];
while (i < maxIter) {
[a, b] = [a * a - b * b + re, 2 * a * b + im];
if (a * a + b * b > 4) break;
i++;
}
const index = ~~((i / maxIter) * (colors.length - 1));
output += colors[index];
}
output += y === rows - 1 ? "" : "\n";
}
echo(output);
}
function map(x, d0, d1, r0, r1) {
return r0 + ((r1 - r0) * (x - d0)) / (d1 - d0);
}
/**
* Again, you don't need to completely understand the code above for now. If
* you find textual outputs can be interesting and creative by this example,
* that's the point!
*
* If you're really curious about Mandelbrot set, here are some examples that
* I made with Charming.js[2] you may find interesting:
*
* - Multibrot Set: https://observablehq.com/d/fc2cfd9ae9e7524c
* - Multibrot Set Table: https://observablehq.com/d/3028c0d5655345e3
* - Multibrot Set Transition: https://observablehq.com/d/c040d3db33c0033e
* - Zoomable Mandelbrot Set (Canvas): https://observablehq.com/d/2e5bdd2365236c2d
* - Zoomable Mandelbrot Set (WebGL): https://observablehq.com/d/cfe263c1213334e3
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* References
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* [1] https://en.wikipedia.org/wiki/Mandelbrot_set
* [2] https://charmingjs.org/
*/