{"id":51002095,"url":"https://github.com/yeet-src/flowball","last_synced_at":"2026-06-20T15:33:08.310Z","repository":{"id":365760736,"uuid":"1261457891","full_name":"yeet-src/flowball","owner":"yeet-src","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-18T18:02:30.000Z","size":9,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-18T20:07:36.633Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yeet-src.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-06T17:58:49.000Z","updated_at":"2026-06-18T18:02:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/yeet-src/flowball","commit_stats":null,"previous_names":["yeet-src/flowball"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/yeet-src/flowball","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeet-src%2Fflowball","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeet-src%2Fflowball/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeet-src%2Fflowball/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeet-src%2Fflowball/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yeet-src","download_url":"https://codeload.github.com/yeet-src/flowball/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yeet-src%2Fflowball/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34576042,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-20T02:00:06.407Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-20T15:33:07.874Z","updated_at":"2026-06-20T15:33:08.305Z","avatar_url":"https://github.com/yeet-src.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flowball — a live newspaper page that flows around bouncing balls\n\nAn article typeset into responsive columns under a masthead and headline.\nThe columns re-fit as you resize — one when narrow, up to four when wide —\nand balls bounce through the body like figures set into the page, the\ncolumns flowing around them flush against both the gutter and the ball,\nclosing up smooth as each ball drifts on.\n\n```\n──────────────────────── THE TERMINAL TIMES ────────────────────────\n\nProse Learns to Flow Around the Stones\nTypeset live by yeet:text · columns re-fit as you resize · 日本語 · 🌊\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nIn a development long awaited   flows around them and carries   prose, ever\nby readers of fixed-width       on.                             flows around\ndispatches, text in the           In a development long           In a deve-\nterminal has learned to move.   awaited by readers of   ●●●●    readers of f-\nWords now part around    ●●●    fixed-width dispatches ●●●●●●   text in the t-\nobstacle set before    ●●●●●●   text in the terminal   ●●●●●●   move. Words n-\nranks behind it, measu   ●●●    move. Words now part     ●●●    obstacle set\nbut in the columns they truly   around any obstacle set         ranks behind\n```\n\n## Run\n\n```sh\nyeet run -T .            # live, sizes to your terminal; resize to re-flow\nyeet run -T . 600 3      # stop after 600 frames, 3 balls (figures)\nyeet run .               # piped (no PTY): prints frames to stdout\n```\n\nArgs are positional: frame cap first (omit for unbounded), then ball count\n(1–8, default 2).\n\n## Why it's a `yeet:text` demo\n\nNewspaper layout is the case `yeet:text` is built for — measure in real\ndisplay columns, wrap, justify, and never split a glyph:\n\n- **`displayWidth`** measures every word once and drives both the column\n  wrap and full justification, so each line fills its measure exactly.\n- **`eachColumn`** blits each line one grapheme cluster at a time, so a\n  wide glyph (CJK, emoji) advances two columns and is never torn across a\n  column edge or stranded at a ball's margin — `👪 🏳️ 東京 日本語 🌊 café`\n  set inline as cleanly as ASCII.\n- **`clip`** trims the masthead, byline, and caption to the page width\n  without cutting a cluster in half.\n\n## How it works\n\n1. **Set the page.** On start and on every `tty.on(\"resize\")`, the header\n   (masthead, wrapping headline, byline, rule) is laid out, the body height\n   that remains is divided into `clamp(⌊(W+gutter)/(target+gutter)⌋, 1, 4)`\n   equal columns, and the article is wrapped continuously into justified\n   lines — paragraphs indented, last lines ragged — then poured down column\n   one, then column two, like real newspaper flow. Each line becomes a slot\n   pinned to a column and row.\n2. **Flow per frame.** Each frame, every slot is re-laid into the free\n   spans left after each ball's exclusion ellipse (drawn radius plus a\n   one-cell padding ring) is cut out of its column. A slot the balls miss\n   renders unchanged — so the page stays stable instead of boiling, and\n   only the text beside a ball moves.\n3. **Drop, don't shift.** Words that no longer fit beside a ball are\n   dropped for that frame rather than pushed to later lines, which keeps\n   every other line anchored. They return as the balls move on.\n4. **The balls** are shaded disks (Lambert diffuse + a tight specular\n   highlight) confined to the body region, drawn over the text into the\n   holes the exclusion ellipses already cleared. Their hue drifts every\n   frame and jumps on every impact. They move chaotically — a constant\n   gentle wander plus a hard random kick off each wall, never a clean\n   straight bounce — and still collide elastically with each other (resolved\n   in a scaled space where each ellipse is a circle, so two figures never\n   merge into one blob). Steering rotates the velocity and renormalises it\n   to a constant speed, so the motion stays lively without winding down or\n   running away.\n\nThe balls are ellipses with a horizontal radius twice the vertical one, so\nthey read round despite terminal cells being about half as wide as tall.\n\n## Known shortcut\n\nThe whole page repaints every frame (no cell diffing, unlike `beachballs`\nor `mathshow`), which is a lot of bytes on a large terminal. The lossy\nreflow in step 3 also means text beside a ball is dropped, not re-wrapped\nonto later lines — a real document renderer would push it down instead.\n\n## Make it something else\n\n- Swap the exclusion shape in `freeSpans()`/`cut()` — a rectangle per ball\n  gives magazine pull-quote boxes instead of round holes.\n- Feed real prose as `PARAS` (a README, an RSS item, a man page) and you\n  have a responsive reader that flows around whatever you set into it.\n- Drop the balls and you have a plain responsive multi-column typesetter.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyeet-src%2Fflowball","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyeet-src%2Fflowball","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyeet-src%2Fflowball/lists"}