Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
258 lines (245 loc) · 34.4 KB
/
Copy pathindex.html
File metadata and controls
258 lines (245 loc) · 34.4 KB
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
<!doctype html>
<!--
===================================================================================
APPLICATION : JS-modules landing page
FORGED BY : LFC Foundry (https://github.com/lfcfoundry)
LICENSE : MIT License
===================================================================================
Copyright (c) 2026 LFC Foundry
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
BRANDING NOTICE: While the code is open under MIT, the name "LFC Foundry" and
associated project identities are reserved. If you fork, significantly modify,
or commercially redistribute this software, you must change the external public
product name and branding.
===================================================================================
-->
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src 'self' data:; base-uri 'none'; form-action 'none'">
<title>JS-modules</title><meta name="description" content="Small, dependency-free pieces for single-file browser games and apps. Copy one file, get one fix.">
<style>
/* Layout: dark hero band, white content body, tiles + bar chart + code sample, like a library landing page. */
:root{
--bg:#ffffff; --fg:#1b2433; --muted:#566277; --soft:#f1f4f9; --line:#dde3ee;
--hero:#0f2a47; --hero-fg:#f3f7fc; --hero-muted:#a9bdd6;
--accent:#1f7ae0; --accent-fg:#ffffff; --good:#1e9e6a; --bar-other:#9aa7bb; --code-bg:#0f1b2d; --code-fg:#dbe7f7;
--display:'Segoe UI',system-ui,sans-serif;
--body:'Segoe UI',system-ui,sans-serif;
--mono:ui-monospace,Consolas,monospace;
}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
--bg:#0b1522; --fg:#e4ebf5; --muted:#9db0c8; --soft:#13233a; --line:#25364f;
--hero:#08111d; --accent:#4c9bf5; --good:#3fc98f; --bar-other:#5d6c85; --code-bg:#050c16; color-scheme:dark}}
:root[data-theme="dark"]{
--bg:#0b1522; --fg:#e4ebf5; --muted:#9db0c8; --soft:#13233a; --line:#25364f;
--hero:#08111d; --accent:#4c9bf5; --good:#3fc98f; --bar-other:#5d6c85; --code-bg:#050c16; color-scheme:dark}
*{box-sizing:border-box}
body{background:var(--bg);color:var(--fg);font:17px/1.6 var(--body)}
a{color:var(--accent)}
:focus-visible{outline:3px solid var(--accent);outline-offset:2px}
.wrap{max-width:1040px;margin:0 auto;padding-left:max(16px,env(safe-area-inset-left,0px));padding-right:max(16px,env(safe-area-inset-right,0px))}
h1,h2,h3{font-family:var(--display);line-height:1.15;text-wrap:balance;margin:0}
.hero{background:var(--hero);color:var(--hero-fg);padding-block:64px 56px}
.hero .eyebrow{font:600 13px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--hero-muted)}
.hero h1{font-size:clamp(44px,8vw,84px);font-weight:800;letter-spacing:-.02em;margin:10px 0 14px}
.hero p.pitch{font-size:clamp(19px,2.6vw,25px);max-width:36em;color:var(--hero-muted);margin:0 0 30px}
.nav{display:flex;flex-wrap:wrap;gap:6px 22px;align-items:center;margin-bottom:44px;font-size:15px}.nav b{font:700 18px var(--display);margin-right:auto}.nav a{color:var(--hero-muted);text-decoration:none}.nav a:hover{color:var(--hero-fg)}
.install{display:flex;flex-wrap:wrap;gap:10px 14px;align-items:center;margin-top:26px;color:var(--hero-muted);font-size:15px}.install code{background:#0000004d;padding:8px 12px;border-radius:4px;font:14px var(--mono);color:var(--hero-fg);overflow-wrap:anywhere}
table{border-collapse:collapse;width:100%;margin-top:22px;font-size:16px}th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line)}th{font:700 14px var(--mono);color:var(--muted)}td.win{color:var(--good);font-weight:600}.scroll{overflow-x:auto}
.cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:20px}.cols h3{font-size:16px;margin-bottom:8px}.cols a{display:block;color:var(--muted);text-decoration:none;font-size:15px;margin-bottom:4px}.cols a:hover{color:var(--accent)}
.btns{display:flex;flex-wrap:wrap;gap:12px}
.btn{display:inline-block;padding:14px 26px;border-radius:6px;font:700 18px var(--display);text-decoration:none;border:2px solid var(--accent);background:var(--accent);color:var(--accent-fg)}
.btn.alt{background:transparent;color:var(--hero-fg);border-color:var(--hero-muted)}
.btn:hover{filter:brightness(1.12)}
.badges{display:flex;flex-wrap:wrap;gap:8px;margin-top:28px}
.badge{font:600 13px var(--mono);padding:5px 11px;border:1px solid #35557a;border-radius:4px;color:var(--hero-muted)}
.strip-badges{margin:0}.strip-badges .badge{border-color:var(--line);color:var(--muted);background:var(--soft)}
section.strip{padding-block:22px 0}section.strip+section{border-top:0}
section{padding-block:56px}
section+section{border-top:1px solid var(--line)}
h2{font-size:clamp(28px,4vw,40px);font-weight:700;margin-bottom:14px}
.lead{max-width:44em;color:var(--muted);margin:0 0 8px}
.uses{padding-left:1.2em}.uses li{margin:0 0 6px}
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:18px;margin-top:28px}
.tile{background:var(--soft);padding:22px;border-radius:8px;min-width:0}
.tile h3{font-size:21px;margin-bottom:6px}
.tile p{margin:0;color:var(--muted);font-size:16px}
.bench{display:grid;gap:26px;margin-top:28px}
.bench h3{font-size:19px;margin-bottom:10px}
.row{display:grid;grid-template-columns:minmax(110px,170px) 1fr;gap:12px;align-items:center;margin-bottom:8px;font-size:15px}
.track{background:var(--soft);border-radius:4px;height:30px;position:relative;min-width:0}
.bar{height:100%;border-radius:4px;background:var(--bar-other);display:flex;align-items:center;justify-content:flex-end;padding-right:8px;font:600 13px var(--mono);color:#fff;min-width:max-content}
.bar.us{background:var(--good)}
.bar.out{overflow:visible;position:relative;min-width:6px;padding-right:0}.bar.out span{position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);white-space:nowrap;color:var(--fg);font-weight:700}
.note{font-size:14px;color:var(--muted);margin-top:6px}
@media (max-width:600px){.bench{gap:34px}.row{grid-template-columns:1fr;gap:5px;margin-bottom:16px}.row>span{font-size:15px;color:var(--muted)}.track{height:34px}.bar{font-size:14px;padding-right:10px}.note{font-size:15px;line-height:1.5}}
.code{background:var(--code-bg);color:var(--code-fg);border-radius:8px;padding:20px;overflow-x:auto;font:15px/1.6 var(--mono);margin-top:22px}
.code .c{color:#7d93b0}.code .k{color:#7cc0ff}.code .s{color:#9fe0b4}
.mods{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;margin-top:26px}
.mod{display:block;border:1px solid var(--line);border-radius:6px;padding:16px;text-decoration:none;color:var(--fg);min-width:0}
.mod:hover{border-color:var(--accent)}
.mod b{font:700 17px var(--display);display:flex;justify-content:space-between;gap:8px}
.mod b span{font:400 12px var(--mono);color:var(--muted)}
.mod small{display:block;color:var(--muted);font-size:14.5px;line-height:1.45;margin-top:4px}
footer{background:var(--soft);padding-block:36px;color:var(--muted);font-size:15px}
footer a{margin-right:16px}
@media (prefers-reduced-motion:no-preference){.btn,.mod{transition:filter .15s,border-color .15s}}
/* Netflow gallery */
.gsec{margin-top:34px}.gh{font:700 22px var(--display);margin:0 0 6px}.gsec .lead{margin:0 0 18px}.lead.m{color:var(--muted)}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr));gap:24px 20px}.g3{grid-template-columns:repeat(auto-fill,minmax(min(100%,240px),1fr))}
#gallery figure{margin:0;min-width:0;display:grid;gap:8px;align-content:start}#gallery figure a{display:block;border-radius:6px;overflow:hidden;background:#0f1520;border:1px solid var(--line)}
#gallery figure img{display:block;width:100%;height:auto;aspect-ratio:960/758;object-fit:cover}.g3 figure img{aspect-ratio:960/740}
#gallery figcaption{display:grid;grid-template-columns:auto 1fr;gap:2px 10px;align-items:baseline;font-size:15px}#gallery figcaption b{font:700 17px var(--display)}#gallery figcaption span{grid-column:1/-1;color:var(--muted)}
.new{grid-column:auto!important;justify-self:start;font:600 11px var(--mono,monospace);letter-spacing:.08em;text-transform:uppercase;color:var(--accent)!important;border:1px solid currentColor;border-radius:4px;padding:0 6px}
.gnote{margin-top:28px;font-size:15px;color:var(--muted)}
</style>
</head><body>
<header class="hero"><div class="wrap">
<nav class="nav"><b>LFC Foundry</b><a href="#pick">Modules</a><a href="#gallery">Gallery</a><a href="demos.html">Demos</a><a href="https://github.com/lfcfoundry/JS-modules">GitHub</a></nav>
<div class="eyebrow">MIT licensed</div>
<h1>JS-modules</h1>
<p class="pitch">Small, dependency-free pieces for single-file browser games and apps. Copy one file, get one fix.</p>
<div class="btns">
<a class="btn" href="#pick">Pick a module</a>
<a class="btn alt" href="demos.html">Live demos</a>
<a class="btn alt" href="https://github.com/lfcfoundry/JS-modules">View on GitHub</a>
</div>
<div class="install"><code><script src="save-kit.js"></script></code><span>or</span><code>const SaveKit = require('./save-kit.js')</code></div>
</div></header>
<section class="strip"><div class="wrap">
<div class="badges strip-badges"><span class="badge">49 modules and tools</span><span class="badge">0 dependencies</span><span class="badge">WebAssembly first, JS fallback</span><span class="badge">MIT</span></div>
</div></section>
<section><div class="wrap">
<h2>What it is and how it works</h2>
<p class="lead">JS-modules is a set of 49 small browser libraries and tools, each solving one problem met while building LFC Foundry's apps: Apple Press, Fancy, the Banana, Pineapple Defense, Story Editor and Netflow Map.</p>
<ul class="lead uses">
<li>Each module is one file with no dependencies: copy it, load it with a script tag or <code>require</code>, call it.</li>
<li>Heavy work runs in WebAssembly inside the same file, usually with a JavaScript fallback.</li>
<li>Every folder has a README with limits and comparisons, a demo page, tests and a changelog.</li>
</ul>
<p class="lead">The modules are for developers; the apps built from them need no install and no special knowledge.</p>
</div></section>
<section><div class="wrap">
<h2>Why it exists</h2>
<p class="lead">Single-file offline apps cannot load code from a server, so any library has to be copied into the file, and its size is part of every download. These modules are plain files with no build step and no dependencies. A module is kept only if it does something the nearest open-source option does not, or does it measurably better, and its README says where the other one wins.</p>
<h3>Where it helps</h3>
<ul class="lead uses">
<li>Offline or single-file web apps and games.</li>
<li>One specific fix without adopting a framework.</li>
<li>Pages where download size and memory matter.</li>
<li>Data-heavy pages (maps, charts, logs) that need compact storage.</li>
<li>Higher-end hardware and larger projects: because the code is efficient, it scales up with the project as far as modern browsers allow.</li>
</ul>
</div></section>
<section><div class="wrap">
<h2>Highlights</h2>
<div class="tiles">
<div class="tile"><h3>One file each</h3><p>Copy only what you need. No build step, no package tree.</p></div>
<div class="tile"><h3>Fast by default</h3><p>Hot paths run in WebAssembly with SIMD, with plain JavaScript when wasm is missing.</p></div>
<div class="tile"><h3>Gives memory back</h3><p>The heavy modules hand memory back after big jobs, by a trim() call or an idle timer.</p></div>
<div class="tile"><h3>Tested in three browsers</h3><p>Firefox, Chromium and WebKitGTK. Every release names the versions it ran in.</p></div>
<div class="tile"><h3>Works with AI helpers</h3><p>AGENTS.md, llms.txt and modules.json describe every piece for people and assistants.</p></div>
<div class="tile"><h3>Files other languages can open</h3><p>lfc-pack files are read by Python and Go as well as the browser, so data packed in a page is not locked in.</p></div>
<div class="tile"><h3>Safe saves in 966 bytes</h3><p>save-kit-lite keeps checked fields and a backup slot in 966 bytes minified and gzipped, for small games.</p></div>
<div class="tile"><h3>Big Excel files, faster</h3><p>zip-open opens Excel files over 16 MB on the page 3 to 24% faster (cloud Firefox, Chromium, WebKitGTK), with the same results.</p></div>
<div class="tile"><h3>Numbers you can check</h3><p>Every number names the browser and machine it ran on, and slower cases are stated.</p></div>
</div>
</div></section>
<section><div class="wrap">
<h2>Quick start</h2>
<p class="lead">Save a game safely in three lines.</p>
<pre class="code"><span class="c"><!-- copy modules/save-kit/save-kit.js next to your page --></span>
<script src=<span class="s">"save-kit.js"</span>></script>
<script>
<span class="k">var</span> kit = SaveKit.create({ key: <span class="s">'mygame'</span>, schema: { score: <span class="s">'num'</span> } });
</script></pre>
</div></section>
<section><div class="wrap">
<h2>Compared with the usual choice</h2>
<p class="lead">lfc-pack 0.4.0 against Parquet on 260 MB of real FHIR records (140,468 rows). Measured 2026.10.31 in the cloud test browsers (Chromium 153, Firefox 158, WebKitGTK 2.52.6) on a shared 4-core machine; medians. Parquet was written natively by pyarrow 26 after a DuckDB 2.0 read of the same JSON (zstd level 3, 4 threads).</p>
<div class="scroll"><table><thead><tr><th>Measure</th><th>Parquet</th><th>lfc-pack</th></tr></thead><tbody><tr><td>Pack time, one thread</td><td>7.44 s</td><td class="win">2.13 to 2.69 s</td></tr><tr><td>Pack time, 4 helpers</td><td>7.44 s</td><td class="win">1.08 to 1.56 s</td></tr><tr><td>File size</td><td>18.74 MB</td><td class="win">8.32 MB</td></tr></tbody></table></div>
<p class="note">2.8 to 3.5 times Parquet's speed on one thread and 4.8 to 6.9 times with 4 helpers, at 44% of its size. Since this run, 0.4.4 packs FHIR 10 to 15% faster, and 0.4.6 makes the same file 7.30 MB, 39% of Parquet's size. Since 0.4.13 the library file itself is 353 KB instead of 789 KB, and 0.4.15 packs event tables 14 to 25% faster and logs 10 to 16% faster on a laptop (Firefox 160, Chromium 156), with byte-identical files.</p>
</div></section>
<section><div class="wrap">
<h2>Smaller and faster than the usual choice</h2>
<p class="lead">Measured against the common alternative. Each chart says where it ran; both come from the module pages.</p>
<div class="bench">
<div>
<h3>memory-compression: size of 300,000 network links (MB, lower is better)</h3>
<div class="row"><span>JSON text</span><div class="track"><div class="bar" style="width:100%"><span>57.3 MB</span></div></div></div>
<div class="row"><span>cbor-x</span><div class="track"><div class="bar" style="width:36.5%"><span>20.9 MB</span></div></div></div>
<div class="row"><span>msgpackr</span><div class="track"><div class="bar" style="width:34.9%"><span>20.0 MB</span></div></div></div>
<div class="row"><span>memory-compression</span><div class="track"><div class="bar us out" style="width:13.7%"><span>7.85 MB</span></div></div></div>
<p class="note">Fast mode, restored exactly, measured on version 2.0.1 (sizes are exact byte counts). About 2.5 times smaller than msgpackr, and it unpacks as fast or faster in Chromium 155, Firefox 158, WebKitGTK 2.52.6. It loses on download size: about 116 KB gzipped in 2.8.2 against 11 KB for msgpackr.</p>
</div>
<div>
<h3>text-columns: read 200,000 CSV rows (ms, lower is better)</h3>
<div class="row"><span>Papa Parse, typed rows</span><div class="track"><div class="bar" style="width:100%"><span>166 ms</span></div></div></div>
<div class="row"><span>Papa Parse, arrays</span><div class="track"><div class="bar" style="width:51.8%"><span>86 ms</span></div></div></div>
<div class="row"><span>d3-dsv, arrays</span><div class="track"><div class="bar" style="width:48.2%"><span>80 ms</span></div></div></div>
<div class="row"><span>text-columns</span><div class="track"><div class="bar us out" style="width:31.3%"><span>52 ms</span></div></div></div>
<p class="note">Laptop, Firefox 160, 16 cores, text-columns 0.5.0, 2026.10.30; 6 columns, 7.9 MB. Papa Parse with typed rows takes 3.2 times as long here and 7 to 12 times as long in the cloud test browsers (Firefox 158, Chromium 155, WebKitGTK 2.52.6).</p>
</div>
<div>
<h3>lfc-pack: size of the library file (KB, lower is better)</h3>
<div class="row"><span>lfc-pack 0.4.12</span><div class="track"><div class="bar" style="width:100%"><span>789 KB</span></div></div></div>
<div class="row"><span>lfc-pack 0.4.13</span><div class="track"><div class="bar us out" style="width:44.7%"><span>353 KB</span></div></div></div>
<p class="note">The built-in WebAssembly programs are stored compressed and the browser unpacks them while it compiles them. Results are the same, and load to first result takes 3 to 11% less time (cloud test browsers, lfc-pack changelog).</p>
</div>
<div>
<h3>Sankey chart of 1,500 items: whole build (ms, lower is better)</h3>
<div class="row"><span>Chromium: d3-sankey</span><div class="track"><div class="bar" style="width:100%"><span>27 ms</span></div></div></div>
<div class="row"><span>Chromium: netflow-sankey</span><div class="track"><div class="bar us out" style="width:44.4%"><span>12 ms</span></div></div></div>
<div class="row"><span>Firefox: d3-sankey</span><div class="track"><div class="bar" style="width:85.2%"><span>23 ms</span></div></div></div>
<div class="row"><span>Firefox: netflow-sankey</span><div class="track"><div class="bar us out" style="width:44.4%"><span>12 ms</span></div></div></div>
<div class="row"><span>WebKitGTK: d3-sankey</span><div class="track"><div class="bar" style="width:92.6%"><span>25 ms</span></div></div></div>
<div class="row"><span>WebKitGTK: netflow-sankey</span><div class="track"><div class="bar us out" style="width:48.1%"><span>13 ms</span></div></div></div>
<p class="note">Netflow Map's Sankey view (netflow-sankey 2.4.0) against d3-sankey 0.12.3, same data, cloud test browsers (Chromium 155, Firefox 158, WebKitGTK 2.52.6). Also only 256 band pieces pass over a bar they do not belong to, against 1,916, and loops are drawn instead of refused. d3-sankey is faster on small charts with default settings.</p>
</div>
<div>
<h3>Layered map of 1,000 items: links crossing as drawn (lower is better)</h3>
<div class="row"><span>dagre</span><div class="track"><div class="bar" style="width:100%"><span>23,965</span></div></div></div>
<div class="row"><span>ELK</span><div class="track"><div class="bar" style="width:94.3%"><span>22,602</span></div></div></div>
<div class="row"><span>netflow-layers</span><div class="track"><div class="bar us" style="width:91%"><span>21,812</span></div></div></div>
<p class="note">Random map of 1,000 items and 1,900 links, Chromium 155; the same counts in Firefox and WebKitGTK. netflow-layers takes 129 ms, ELK 2.1 s and dagre 8.4 s. At 400 items the three are level.</p>
</div>
</div>
</div></section>
<section id="gallery"><div class="wrap">
<h2>Netflow Map gallery</h2>
<p class="lead">Screenshots from the Netflow Map app, taken in the app with nothing touched up: its 16 map views and 22 charts. The drawing code is in these modules, so you can use the same views in your own page: <a href="modules/netflow-engine/index.html">netflow-engine</a>, <a href="modules/netflow-hierarchy/index.html">netflow-hierarchy</a> (Treemap, Sunburst, Icicle, Circle pack, Tree), <a href="modules/netflow-circular/index.html">netflow-circular</a> (Chord, Bundles), <a href="modules/netflow-plot/index.html">netflow-plot</a> (plain charts), <a href="modules/netflow-sankey/index.html">netflow-sankey</a> and <a href="modules/netflow-layers/index.html">netflow-layers</a>. Click a picture to see it full size.</p>
<div class="gsec" id="g-story"><h3 class="gh">Stories and processes</h3><p class="lead m">Data with a start and an end: a choose-your-path story, a team's work queue and a supply chain from suppliers to markets.</p><div class="grid"><figure><a href="netflow-gallery/flow.webp"><img src="netflow-gallery/flow.webp" alt="Flow example" width="960" height="758" loading="lazy"></a><figcaption><b>Flow</b><span>Left to right: how things lead on from the start.</span></figcaption></figure><figure><a href="netflow-gallery/down.webp"><img src="netflow-gallery/down.webp" alt="Top-down example" width="960" height="758" loading="lazy"></a><figcaption><b>Top-down</b><span>The same drawing, top to bottom.</span></figcaption></figure><figure><a href="netflow-gallery/spine.webp"><img src="netflow-gallery/spine.webp" alt="Choke-point spine example" width="960" height="758" loading="lazy"></a><figcaption><b>Choke-point spine</b><span>Places every route to the goal must pass, held on one line.</span></figcaption></figure><figure><a href="netflow-gallery/shape.webp"><img src="netflow-gallery/shape.webp" alt="Shape example" width="960" height="758" loading="lazy"></a><figcaption><b>Shape</b><span>The stretches between choke points, each named by its pattern.</span></figcaption></figure><figure><a href="netflow-gallery/lanes.webp"><img src="netflow-gallery/lanes.webp" alt="Lanes example" width="960" height="758" loading="lazy"></a><figcaption><b>Lanes</b><span>One band per group; items in several groups sit in the middle.</span></figcaption></figure><figure><a href="netflow-gallery/sankey.webp"><img src="netflow-gallery/sankey.webp" alt="Sankey example" width="960" height="758" loading="lazy"></a><figcaption><b>Sankey</b><span>Simulated walkers pour through; the wider the band, the more take that arrow.</span></figcaption></figure></div></div>
<div class="gsec" id="g-links"><h3 class="gh">Links between things</h3><p class="lead m">Data without a story: a company's 288 services and the calls between them, and the story again as an arc and a grid.</p><div class="grid"><figure><a href="netflow-gallery/network.webp"><img src="netflow-gallery/network.webp" alt="Network example" width="960" height="758" loading="lazy"></a><figcaption><b>Network</b><span>Closeness on the page follows closeness in the data.</span></figcaption></figure><figure><a href="netflow-gallery/arc.webp"><img src="netflow-gallery/arc.webp" alt="Arc example" width="960" height="758" loading="lazy"></a><figcaption><b>Arc</b><span>Everything on one line; arcs show the links, long arcs are long jumps.</span></figcaption></figure><figure><a href="netflow-gallery/matrix.webp"><img src="netflow-gallery/matrix.webp" alt="Matrix example" width="960" height="758" loading="lazy"></a><figcaption><b>Matrix</b><span>A grid of who links to whom; patterns show clusters and loops.</span></figcaption></figure><figure><a href="netflow-gallery/bundles.webp"><img src="netflow-gallery/bundles.webp" alt="Bundled links example" width="960" height="758" loading="lazy"></a><figcaption><b>Bundled links</b><span class="new">New</span><span>Items around a circle, links bent along the tree so links that go the same way run together.</span></figcaption></figure><figure><a href="netflow-gallery/chord.webp"><img src="netflow-gallery/chord.webp" alt="Chord example" width="960" height="758" loading="lazy"></a><figcaption><b>Chord</b><span class="new">New</span><span>Who sends how much to whom, around a circle; each band is as wide as the amount it carries.</span></figcaption></figure></div></div>
<div class="gsec" id="g-nested"><h3 class="gh">Nested data</h3><p class="lead m">Anything with parents and children: here a bulleted trip-planning outline. The nesting comes from the links, the groups or a parent column.</p><div class="grid"><figure><a href="netflow-gallery/treemap.webp"><img src="netflow-gallery/treemap.webp" alt="Treemap example" width="960" height="758" loading="lazy"></a><figcaption><b>Treemap</b><span class="new">New</span><span>Nested boxes: each box is as big as what it holds.</span></figcaption></figure><figure><a href="netflow-gallery/icicle.webp"><img src="netflow-gallery/icicle.webp" alt="Icicle example" width="960" height="758" loading="lazy"></a><figcaption><b>Icicle</b><span class="new">New</span><span>Steps down from left to right; each bar as tall as what it holds.</span></figcaption></figure><figure><a href="netflow-gallery/sunburst.webp"><img src="netflow-gallery/sunburst.webp" alt="Sunburst example" width="960" height="758" loading="lazy"></a><figcaption><b>Sunburst</b><span class="new">New</span><span>Rings out from the middle: each ring is one step down, each slice as wide as what it holds.</span></figcaption></figure><figure><a href="netflow-gallery/pack.webp"><img src="netflow-gallery/pack.webp" alt="Circle packing example" width="960" height="758" loading="lazy"></a><figcaption><b>Circle packing</b><span class="new">New</span><span>Circles inside circles: each circle is as big as what it holds.</span></figcaption></figure><figure><a href="netflow-gallery/tree.webp"><img src="netflow-gallery/tree.webp" alt="Tidy tree example" width="960" height="758" loading="lazy"></a><figcaption><b>Tidy tree</b><span class="new">New</span><span>One parent for each item, drawn as a tidy tree.</span></figcaption></figure></div></div>
<div class="gsec" id="g-charts"><h3 class="gh">The Charts tab</h3><p class="lead m">22 plain charts from any table's columns, drawn with the same engine as the map. Shown here on a sample of 400 stores with region, team, month, revenue, cost, users, rating and a one-word note. Every chart picks sensible columns on its own, names its colors in a key and saves as SVG.</p><div class="grid g3"><figure><a href="netflow-gallery/c-bar.webp"><img src="netflow-gallery/c-bar.webp" alt="Bars example" width="960" height="740" loading="lazy"></a><figcaption><b>Bars</b><span>Totals for each category; stack, group or share by a second column.</span></figcaption></figure><figure><a href="netflow-gallery/c-line.webp"><img src="netflow-gallery/c-line.webp" alt="Lines example" width="960" height="740" loading="lazy"></a><figcaption><b>Lines</b><span>A number over time or over another number; one line per color.</span></figcaption></figure><figure><a href="netflow-gallery/c-area.webp"><img src="netflow-gallery/c-area.webp" alt="Areas example" width="960" height="740" loading="lazy"></a><figcaption><b>Areas</b><span>Lines filled down, stacked, as shares or as a stream.</span></figcaption></figure><figure><a href="netflow-gallery/c-scatter.webp"><img src="netflow-gallery/c-scatter.webp" alt="Scatter example" width="960" height="740" loading="lazy"></a><figcaption><b>Scatter</b><span>Each row a dot at two numbers; size and color from more columns.</span></figcaption></figure><figure><a href="netflow-gallery/c-connected.webp"><img src="netflow-gallery/c-connected.webp" alt="Connected scatter example" width="960" height="740" loading="lazy"></a><figcaption><b>Connected scatter</b><span>Dots joined in row order, to show a path through two numbers.</span></figcaption></figure><figure><a href="netflow-gallery/c-beeswarm.webp"><img src="netflow-gallery/c-beeswarm.webp" alt="Beeswarm example" width="960" height="740" loading="lazy"></a><figcaption><b>Beeswarm</b><span>Every row a dot along one number, dots pushed apart so none hide.</span></figcaption></figure><figure><a href="netflow-gallery/c-dotplot.webp"><img src="netflow-gallery/c-dotplot.webp" alt="Dot plot example" width="960" height="740" loading="lazy"></a><figcaption><b>Dot plot</b><span>One dot per category at its value, easy to compare and sort.</span></figcaption></figure><figure><a href="netflow-gallery/c-hist.webp"><img src="netflow-gallery/c-hist.webp" alt="Histogram example" width="960" height="740" loading="lazy"></a><figcaption><b>Histogram</b><span>How a number is spread: rows counted in equal bins.</span></figcaption></figure><figure><a href="netflow-gallery/c-box.webp"><img src="netflow-gallery/c-box.webp" alt="Box plot example" width="960" height="740" loading="lazy"></a><figcaption><b>Box plot</b><span>Middle half, median and range of a number for each category.</span></figcaption></figure><figure><a href="netflow-gallery/c-density.webp"><img src="netflow-gallery/c-density.webp" alt="Density example" width="960" height="740" loading="lazy"></a><figcaption><b>Density</b><span>A smooth curve of how a number is spread, one per category.</span></figcaption></figure><figure><a href="netflow-gallery/c-ridgeline.webp"><img src="netflow-gallery/c-ridgeline.webp" alt="Ridgeline example" width="960" height="740" loading="lazy"></a><figcaption><b>Ridgeline</b><span>Density curves stacked one above the other, one per category.</span></figcaption></figure><figure><a href="netflow-gallery/c-hexbin.webp"><img src="netflow-gallery/c-hexbin.webp" alt="Hexbin example" width="960" height="740" loading="lazy"></a><figcaption><b>Hexbin</b><span>Two numbers counted in hexagons: where rows bunch up.</span></figcaption></figure><figure><a href="netflow-gallery/c-heat.webp"><img src="netflow-gallery/c-heat.webp" alt="Heat map example" width="960" height="740" loading="lazy"></a><figcaption><b>Heat map</b><span>A grid of two categories (or days of a date column), colored by value.</span></figcaption></figure><figure><a href="netflow-gallery/c-donut.webp"><img src="netflow-gallery/c-donut.webp" alt="Donut example" width="960" height="740" loading="lazy"></a><figcaption><b>Donut</b><span>A pie with a hole; the total sits in the middle.</span></figcaption></figure><figure><a href="netflow-gallery/c-radialbar.webp"><img src="netflow-gallery/c-radialbar.webp" alt="Radial bars example" width="960" height="740" loading="lazy"></a><figcaption><b>Radial bars</b><span>Bars around a circle, stacked by a second column.</span></figcaption></figure><figure><a href="netflow-gallery/c-parallel.webp"><img src="netflow-gallery/c-parallel.webp" alt="Parallel lines example" width="960" height="740" loading="lazy"></a><figcaption><b>Parallel lines</b><span>Every row a line across several number columns.</span></figcaption></figure><figure><a href="netflow-gallery/c-splom.webp"><img src="netflow-gallery/c-splom.webp" alt="Scatter matrix example" width="960" height="740" loading="lazy"></a><figcaption><b>Scatter matrix</b><span>Every pair of number columns as a small scatter.</span></figcaption></figure><figure><a href="netflow-gallery/c-qq.webp"><img src="netflow-gallery/c-qq.webp" alt="Q-Q plot example" width="960" height="740" loading="lazy"></a><figcaption><b>Q-Q plot</b><span>A number against the normal curve: straight means normal.</span></figcaption></figure><figure><a href="netflow-gallery/c-slope.webp"><img src="netflow-gallery/c-slope.webp" alt="Slope chart example" width="960" height="740" loading="lazy"></a><figcaption><b>Slope chart</b><span>How each category changed between two values of a column.</span></figcaption></figure><figure><a href="netflow-gallery/c-marimekko.webp"><img src="netflow-gallery/c-marimekko.webp" alt="Marimekko example" width="960" height="740" loading="lazy"></a><figcaption><b>Marimekko</b><span>Bars as wide as their total, split by a second column.</span></figcaption></figure><figure><a href="netflow-gallery/c-voronoi.webp"><img src="netflow-gallery/c-voronoi.webp" alt="Voronoi example" width="960" height="740" loading="lazy"></a><figcaption><b>Voronoi</b><span>Each dot with the area nearer to it than to any other.</span></figcaption></figure><figure><a href="netflow-gallery/c-words.webp"><img src="netflow-gallery/c-words.webp" alt="Word cloud example" width="960" height="740" loading="lazy"></a><figcaption><b>Word cloud</b><span>The words of a text column, bigger when more common.</span></figcaption></figure></div></div>
<p class="gnote">Taken in ungoogled Chromium, dark theme, Netflow Map build 2026.11.01.1003 (Sankey: 1045). The chart pictures use a made-up sample table of 400 stores.</p>
</div></section>
<section id="pick"><div class="wrap">
<h2>Pick a piece</h2>
<p class="lead">A small selection. The full list of 49 is in the repository README.</p>
<div class="mods">
<a class="mod" href="https://github.com/lfcfoundry/JS-modules/blob/main/modules/save-kit/README.md"><b>save-kit</b><small>Safe saves with a backup slot, checksums and copy-paste save codes.</small></a>
<a class="mod" href="https://github.com/lfcfoundry/JS-modules/blob/main/modules/fx-governor/README.md"><b>fx-governor</b><small>Steps effects down when the device cannot keep up.</small></a>
<a class="mod" href="https://github.com/lfcfoundry/JS-modules/blob/main/modules/lfc-pack/README.md"><b>lfc-pack</b><small>Packs NDJSON into small column files and reads rows back.</small></a>
<a class="mod" href="https://github.com/lfcfoundry/JS-modules/blob/main/modules/gif-writer/README.md"><b>gif-writer</b><small>Animated GIFs from canvas frames with one shared palette.</small></a>
<a class="mod" href="https://github.com/lfcfoundry/JS-modules/blob/main/modules/stage-curtains/README.md"><b>stage-curtains</b><small>Velvet curtains that slide shut over a page and fly open.</small></a>
<a class="mod" href="https://github.com/lfcfoundry/JS-modules/blob/main/modules/netflow-engine/README.md"><b>netflow-engine</b><small>Nine views and network layouts as an adaptable module.</small></a>
</div>
</div></section>
<footer><div class="wrap">
<div class="cols">
<div><h3>Modules</h3><a href="#pick">Pick a piece</a><a href="#gallery">Netflow gallery</a><a href="demos.html">Live demos</a></div>
<div><h3>Documentation</h3><a href="https://github.com/lfcfoundry/JS-modules/blob/main/CHANGELOG.md">Changelog</a><a href="https://github.com/lfcfoundry/JS-modules/blob/main/AGENTS.md">AGENTS.md</a></div>
<div><h3>Community</h3><a href="https://github.com/lfcfoundry/JS-modules/issues">Issues</a><a href="https://github.com/lfcfoundry/JS-modules">Source code</a></div>
<div><h3>More from LFC Foundry</h3><a href="https://github.com/lfcfoundry/apple-press">Apple Press</a><a href="https://github.com/lfcfoundry/fancy-banana">Fancy Banana</a><a href="https://github.com/lfcfoundry/story-editor">Story Editor</a><a href="https://github.com/lfcfoundry/pineapple-defense">Pineapple Defense</a></div>
</div>
<p>MIT License. Copyright (c) 2026 LFC Foundry.</p>
<p>Forged by LFC Foundry • MIT Licensed</p>
</div></footer>
</body></html>