File size: 43,182 Bytes
83cf288
e6863b5
 
 
 
 
943fb39
e6863b5
943fb39
e6863b5
943fb39
 
 
 
 
 
 
 
 
 
 
 
 
e6863b5
943fb39
 
 
 
 
 
e6863b5
 
 
 
757768f
 
13a1396
943fb39
 
 
 
 
 
 
 
 
 
 
 
 
e6863b5
 
 
943fb39
757768f
 
 
 
 
 
 
 
 
 
 
 
 
e6863b5
 
757768f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e6863b5
 
943fb39
 
 
 
 
e6863b5
943fb39
e6863b5
 
 
 
 
 
 
 
943fb39
 
 
 
e6863b5
 
943fb39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e6863b5
 
 
 
943fb39
 
e6863b5
 
 
943fb39
 
 
 
 
 
 
 
 
 
 
757768f
943fb39
 
 
 
 
 
35d922f
e6863b5
943fb39
 
 
e5067cd
943fb39
 
 
 
e6863b5
 
 
 
 
 
 
 
10880af
943fb39
 
e6863b5
 
 
943fb39
e6863b5
 
943fb39
 
 
e6863b5
 
 
10880af
e6863b5
 
 
10880af
e6863b5
 
 
943fb39
e6863b5
 
 
 
 
 
943fb39
 
e6863b5
 
943fb39
 
 
 
 
 
e6863b5
 
 
943fb39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7391956
943fb39
 
 
 
e6863b5
943fb39
 
e6863b5
 
 
943fb39
e6863b5
757768f
943fb39
 
 
e5067cd
943fb39
 
 
 
e5067cd
943fb39
 
 
 
 
 
 
 
 
 
e5067cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc6b6d8
e6863b5
 
757768f
 
 
13a1396
757768f
 
 
 
 
e6863b5
83cf288
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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Unroll · PHerc. 139</title>
<meta name="description" content="Unroll a carbonized Herculaneum scroll in your browser: 37 windings traced in X-ray CT. Ink layers show where models detect ink; letters visible here are candidates for expert reading.">
<meta property="og:title" content="Unroll · a Herculaneum scroll, in your browser">
<meta property="og:description" content="37 windings of PHerc. 139, traced in X-ray CT. Ink layers show where models detect ink; letters visible here are candidates for expert reading.">
<meta property="og:image" content="https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/og.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Carbonized papyrus sheets of PHerc. 139 laid flat in 3D, with amber ink-layer glow">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Unroll">
<meta property="og:url" content="https://huggingface.co/spaces/SharpMeow/unroll">
<link rel="canonical" href="https://huggingface.co/spaces/SharpMeow/unroll">
<meta name="twitter:title" content="Unroll · a Herculaneum scroll, in your browser">
<meta name="twitter:description" content="37 windings of PHerc. 139, traced in X-ray CT. Ink layers show where models detect ink; letters visible here are candidates for expert reading.">
<meta name="twitter:image" content="https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/og.jpg">
<meta name="twitter:image:alt" content="Carbonized papyrus sheets of PHerc. 139 laid flat in 3D, with amber ink-layer glow">
<meta name="theme-color" content="#060505">
<meta name="color-scheme" content="dark">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%23060505'/%3E%3Cpath d='M16 6a10 10 0 1 0 0.01 0M16 10a6 6 0 1 1-0.01 0M16 14a2 2 0 1 0 0.01 0' fill='none' stroke='%23ffb347' stroke-width='1.6'/%3E%3C/svg%3E">
<link rel="manifest" href="manifest.webmanifest">
<link rel="apple-touch-icon" href="icons/apple-touch-icon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Unroll">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="twitter:card" content="summary_large_image">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css?v=20261027one1">
<link rel="stylesheet" href="bench.css?v=20261027one1">
<link rel="stylesheet" href="ui.css?v=20261028phone1">
<link rel="alternate" type="application/json+oembed" href="https://sharpmeow-unroll.static.hf.space/oembed.json" title="Unroll">
<script>
// embed mode is decided before the first paint so the compact layout never flashes
(function () { var q = location.search; if (/[?&]embed=(1|true|yes)\b/.test(q)) { document.documentElement.classList.add('embed'); if (/[?&]controls=(0|false|no)\b/.test(q)) document.documentElement.classList.add('embed-nocontrols'); } })();
</script>
<!-- start the big downloads and the 3D library in parallel with the page instead of one after another -->
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js" crossorigin>
<script>
// the default scroll's big files, fetched while three.js loads; skipped when the URL opens another scroll (#s=...)
if (!/(^|[#&])s=/.test(location.hash)) ['scrolls/index.json', 'scrolls/PHerc0139/manifest.json', 'data/scroll.json', 'data/scroll.i16p.gz', 'data/atlas_ink.webp', 'data/atlas_ct.webp'].forEach(function (h) {
  var l = document.createElement('link'); l.rel = 'preload'; l.as = 'fetch'; l.crossOrigin = 'anonymous'; l.href = h; document.head.appendChild(l);
});
</script>
<script type="importmap">
{ "imports": {
  "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
  "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/",
  "./lib/ctsource.js": "./lib/ctsource.js?v=20261027one1",
  "./lib/scrolls.js": "./lib/scrolls.js?v=20261027one1",
  "./lib/manifest.js": "./lib/manifest.js?v=20261027one1",
  "./lib/openown.js": "./lib/openown.js?v=20261027one1",
  "./lib/formats.js": "./lib/formats.js?v=20261027one1",
  "./lib/example.js": "./lib/example.js?v=20261027one1",
  "./lib/bench-core.js": "./lib/bench-core.js?v=20261027one1",
  "./lib/api.js": "./lib/api.js?v=20261027one1",
  "./lib/apispec.js": "./lib/apispec.js?v=20261027one1",
  "./lib/embed.js": "./lib/embed.js?v=20261027one1",
  "./lib/version.js": "./lib/version.js?v=20261027one1",
  "./tear.js": "./tear.js?v=20261027one1",
  "./damage.js": "./damage.js?v=20261027one1"
} }
</script>
<!-- space-onescroll:start -->
<!-- one page scroll bar: written by python -m kit spaceconnect render; do not edit -->
<style>iframe[srcdoc]{display:block;max-height:none!important}</style>
<script>
(function () {
  if (window.__oneScroll) return;
  window.__oneScroll = true;
  var CSS = 'html,body{height:auto!important;min-height:0!important}html{overflow:hidden!important}';
  function size(f) {
    var d, st = f.__os;
    try { d = f.contentDocument; } catch (e) { return; }
    if (!d || !d.body || st.off) return;
    var de = d.documentElement, r = Math.ceil(de.getBoundingClientRect().height), over = de.scrollHeight;
    var ch = f.clientHeight, t = r;
    if (over > ch + 1) t = Math.max(r, over);
    else if (r >= st.r) t = Math.max(r, ch);
    st.r = r;
    if (Math.abs(t - ch) < 2) return;
    var now = Date.now();
    st.n = now - st.t0 < 4000 ? st.n + 1 : 1;
    if (st.n === 1) st.t0 = now;
    if (st.n > 40) { st.off = true; f.removeAttribute('scrolling'); var g = d.getElementById('space-onescroll'); if (g) g.remove(); return; }
    f.style.setProperty('height', t + 'px', 'important');
  }
  function attach(f) {
    var d, st = f.__os;
    try { d = f.contentDocument; } catch (e) { return; }
    if (!d || !d.documentElement || st.doc === d) return;
    st.doc = d; st.r = Infinity; st.n = 0; st.off = false;
    if (!d.getElementById('space-onescroll')) {
      var s = d.createElement('style'); s.id = 'space-onescroll'; s.textContent = CSS;
      (d.head || d.documentElement).appendChild(s);
    }
    f.setAttribute('scrolling', 'no');
    f.style.setProperty('min-height', '0', 'important');
    f.style.setProperty('max-height', 'none', 'important');
    var w = d.defaultView, go = function () { size(f); };
    if (w && w.ResizeObserver) { var ro = new w.ResizeObserver(go); ro.observe(d.documentElement); if (d.body) ro.observe(d.body); }
    d.addEventListener('load', go, true);
    go();
  }
  function bind(f) {
    if (f.__os || f.getAttribute('data-scroll') === 'inner') return;
    try { if (!f.contentDocument) return; } catch (e) { return; }
    f.__os = {t0: 0, n: 0, r: 0};
    f.addEventListener('load', function () { attach(f); });
    if (f.contentDocument.readyState !== 'loading') attach(f);
    else f.contentDocument.addEventListener('DOMContentLoaded', function () { attach(f); });
  }
  function scan() { var fs = document.getElementsByTagName('iframe'); for (var i = 0; i < fs.length; i++) bind(fs[i]); }
  function start() {
    scan();
    new MutationObserver(scan).observe(document.documentElement, {childList: true, subtree: true});
    window.addEventListener('resize', function () {
      var fs = document.getElementsByTagName('iframe');
      for (var i = 0; i < fs.length; i++) if (fs[i].__os) size(fs[i]);
    });
  }
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start); else start();
})();
</script>
<!-- space-onescroll:end -->
</head>
<body>
<canvas id="gl" tabindex="0" role="application" aria-roledescription="3D view" aria-label="3D view of the scroll. Arrow keys orbit, Shift and arrows pan, plus and minus zoom, Enter opens the close-up of the current winding." aria-describedby="viewDesc" data-testid="view-3d" data-api="camera.orbit()"></canvas>
<button id="embedGate" class="embed-gate" hidden data-testid="embed-activate" data-api="embed.activate()" aria-label="Activate the 3D viewer: after this, dragging, pinching and the mouse wheel control the scroll"><span class="mono">Click to interact</span></button>
<a id="embedFull" class="embed-full mono" href="./" target="_blank" rel="noopener" data-testid="embed-open-full" data-api="deepLink()">Open full viewer ↗</a>
<p id="viewDesc" class="sr-only" data-testid="view-description">Loading the scroll.</p>
<div id="liveState" class="sr-only" role="status" aria-live="polite" aria-atomic="true" data-testid="live-state"></div>
<div class="scrim top" aria-hidden="true"></div><div class="scrim bot" aria-hidden="true"></div>
<div class="vignette" aria-hidden="true"></div>
<div class="grain" aria-hidden="true"></div>

<div id="loader">
  <div class="loader-inner">
    <div class="mono dim">PHERC. 0139 · ESRF · 9.362 µm</div>
    <div class="loader-title">Reconstructing the scroll</div>
    <div class="bar"><i id="loadbar"></i></div>
    <div class="mono dim" id="loadtxt">0 MB</div>
    <div class="load-err" id="loadErr" hidden>
      <p>The scroll could not be loaded. <span id="loadErrWhy"></span></p>
      <button class="chip" onclick="location.reload()" data-testid="load-retry" data-api="reload()">Try again</button>
    </div>
  </div>
</div>
<script>
// empty and error states: if the scroll data or 3D graphics fail, say so instead of loading forever
(function () {
  function fail(e) {
    var box = document.getElementById('loadErr');
    if (!box || !document.getElementById('loader')) return;
    var msg = String((e && (e.reason || e.error || e.message)) || '');
    var why = window.__unrollWhy ? window.__unrollWhy : /webgl|context/i.test(msg) ? 'Your browser could not start 3D graphics (WebGL). Try another browser or turn on hardware acceleration.'
      : 'A data file did not arrive. Check your connection and try again.';
    document.getElementById('loadErrWhy').textContent = why;
    box.hidden = false;
    document.getElementById('loader').classList.add('failed');
  }
  addEventListener('unhandledrejection', fail);
  addEventListener('error', function (e) { if (e.filename && /app\.js|tear\.js|lib\/|three/.test(e.filename)) fail(e); });
})();
</script>

<header class="hud tl">
  <div class="mono dim kicker">PHERC. 0139 <span class="sep">/</span> HERCULANEUM <span class="sep">/</span> BURIED 79 CE</div>
  <h1>Unroll a scroll nobody has opened in <em>1,947 years.</em></h1>
  <p class="lede">37 of its windings have been traced through X-ray CT by the Vesuvius Challenge. Ink layers show where models detect ink; letters visible here are candidates for expert reading. Drag to look around. Scrub to unroll.</p>
  <div class="scroll-pick" id="scrollPick" hidden><label class="mono dim" for="scrollSel">SCROLL</label> <select id="scrollSel" aria-label="Choose a scroll" data-testid="scroll-picker" data-api="openScroll()"></select> <a class="prize-badge" id="prizeBadge" data-testid="prize-badge" data-api="links()" hidden href="https://scrollprize.org/prizes" target="_blank" rel="noopener" title="An open Vesuvius Challenge prize can be won on this scroll. See the prizes page">🏆 prize eligible</a> <button class="chip prize-only" id="prizeOnly" data-testid="prize-only" data-api="prizeOnly()" hidden aria-pressed="false" title="Show only scrolls marked prize eligible on scrollprize.org">🏆 prize eligible only</button></div>
</header>

<nav class="hud tr" aria-label="Layers">
  <div class="seg" role="tablist" aria-label="Layer">
    <button class="chip" data-layer="xray" role="tab" aria-selected="false" data-testid="layer-xray" data-api="setLayer('xray')" title="Raw X-ray CT brightness on each winding. No ink detection.">X‑ray</button>
    <button class="chip on" data-layer="ink" role="tab" aria-selected="true" data-testid="layer-ink" data-api="setLayer('ink')" title="Vesuvius Challenge's published ink detection on the fine 2.4 µm scan, covering all 37 windings.">Published ink (2.4 <span class="mu">µ</span>m)</button>
    <button class="chip gpu" data-layer="gpu" role="tab" aria-selected="false" id="gpuChip" data-testid="layer-overlay" data-api="setLayer('overlay')" hidden title="Lower resolution comparison. Chase Hendrick ran the public scrollprize/ink_9um model, which the Vesuvius Challenge trained, on the coarser 9.4 µm scan (about 4x coarser than 2.4 µm), for all 37 windings. It looks blurrier and shows patch seams. It is here to show what an open model can recover from cheaper, more widely available scans, as a reproducible baseline you can check against the published result."><i class="dot"></i>ink_9um (9 <span class="mu">µ</span>m)</button>
  </div>
  <p class="layer-hint" id="layerHint" aria-live="polite">Published ink (2.4 µm): Vesuvius Challenge detection, 2.4 µm scan, all windings.</p>
  <details class="layer-why" id="layerWhy"><summary data-testid="layer-why" data-api="panel('why')">Why two ink layers?</summary><p>Published ink is the Vesuvius Challenge result on the fine 2.4 µm scan. ink_9um (9 µm) comes from the public scrollprize/ink_9um model, which the Vesuvius Challenge trained; Chase Hendrick ran it on a scan about 4x coarser, so it is blurrier. It shows what an open model can recover from cheaper scans, as a baseline you can check.</p></details>
  <div class="seg tear-seg">
    <button class="chip" id="btnTear" data-testid="destroy-mode" data-api="destroy.mode()" aria-pressed="false" title="Destroy: tear, crush, burn or soak the scroll, rolled up or unrolled. Damage stays as you roll and unroll. Just for fun, not a real unrolling. (T)">Destroy</button>
    <button class="chip tool" id="toolTear" data-tool="tear" data-testid="destroy-tool-tear" data-api="destroy.tool('tear')" hidden aria-pressed="false" title="Drag across the papyrus to rip it (X)">Tear</button>
    <button class="chip tool" id="btnCrush" data-tool="crush" data-testid="destroy-tool-crush" data-api="destroy.tool('crush')" hidden aria-pressed="false" title="Press to crumble it; hold to press harder and go deeper into the roll (C)">Crush</button>
    <button class="chip tool" id="toolBurn" data-tool="burn" data-testid="destroy-tool-burn" data-api="destroy.tool('burn')" hidden aria-pressed="false" title="Click to ignite: embers creep slowly along the fibers (B)">Burn</button>
    <button class="chip tool" id="toolWater" data-tool="water" data-testid="destroy-tool-water" data-api="destroy.tool('water')" hidden aria-pressed="false" title="Press or hold to drip and pour water: it darkens, softens, weakens and slowly dissolves the papyrus (W)">Water</button>
    <button class="chip" id="btnReset" data-testid="destroy-reset" data-api="destroy.reset()" hidden title="Undo all damage and clear the debris">Reset</button>
    <button class="chip" id="btnCrumble" data-testid="destroy-crumble" data-api="destroy.crumble()" hidden title="Crumble the whole scroll: it breaks up from the edges and falls into a heap of dust. Reset restores it. (K)">Crumble</button>
    <button class="chip" id="btnSandbox" data-testid="destroy-sandbox" data-api="destroy.sandbox()" hidden aria-pressed="false" title="Lay the strip flat with full fracture physics: grab to rip, Crush to crumble">Sandbox</button>
    <button class="chip" id="btnShake" data-testid="destroy-shake" data-api="destroy.shake()" hidden title="Knock the table: loosens random bits">Shake</button>
    <button class="chip" id="btnSound" data-testid="destroy-sound" data-api="sound()" hidden aria-pressed="false" title="Soft crackle when it breaks">Sound off</button>
  </div>
  <div class="tools">
    <button class="icon" id="btnOpen" data-testid="open-own" data-api="panel('open')" title="Open your own scroll: files stay in your browser, nothing is uploaded" aria-label="Open your own scroll">+</button>
    <button class="icon" id="btnBench" data-testid="bench" data-api="bench.open()" title="Ink Bench: score your own ink prediction against the published ink. Runs in your browser, nothing is uploaded" aria-label="Ink Bench"><svg viewBox="0 0 24 24" width="15" height="15"><path d="M4 19V9M10 19V5M16 19v-7M21 19H3" fill="none" stroke="currentColor" stroke-width="1.6"/></svg></button>
    <button class="icon" id="btnInfo" data-testid="about" data-api="panel('about')" title="About this page" aria-label="About">i</button>
    <button class="icon" id="btnEmbed" data-testid="embed" data-api="panel('embed')" title="Embed this viewer on your site, or connect your tool to its data" aria-label="Embed this viewer or connect your tool">&lt;/&gt;</button>
    <button class="icon" id="btnFull" data-testid="fullscreen" data-api="fullscreen()" title="Fullscreen" aria-label="Fullscreen" aria-pressed="false">
      <svg viewBox="0 0 24 24" width="15" height="15"><path d="M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>
    </button>
    <button class="icon" id="btnShot" data-testid="screenshot" data-api="screenshot()" title="Save a picture" aria-label="Save a picture">
      <svg viewBox="0 0 24 24" width="15" height="15"><path d="M4 8h3l2-3h6l2 3h3v11H4z" fill="none" stroke="currentColor" stroke-width="1.5"/><circle cx="12" cy="13" r="3.5" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>
    </button>
  </div>
</nav>

<aside class="hud bl readout mono" id="readout">
  <div><span class="dim">WINDING</span> <b id="rWind">w059</b></div>
  <div><span class="dim">RADIUS</span> <b id="rRad">13.3 mm</b></div>
  <div><span class="dim">STRIP</span> <b id="rLen">0.00 m</b></div>
  <div id="rGpuRow" hidden><span class="dim">INK_9UM</span> <b id="rGpu">·</b></div>
  <button class="chip read" id="btnRead" data-testid="read-winding" data-api="focus()">Read w059</button>
</aside>

<footer class="hud bottom">
  <button id="play" aria-pressed="false" data-testid="play" data-api="play()" class="play" aria-label="Unroll">
    <svg viewBox="0 0 24 24" width="16" height="16" id="playIcon"><path d="M7 5l12 7-12 7z" fill="currentColor"/></svg>
  </button>
  <button id="btnRollUp" class="chip roll-up" data-testid="roll-up" data-api="rollUp()" aria-pressed="false" title="Roll it back up (U)">Roll up</button>
  <div class="scrub" id="scrub" data-testid="scrubber">
    <input type="range" id="scrubIn" class="scrub-in" min="0" max="1000" step="1" value="0" aria-label="Unroll position" data-testid="unroll-slider" data-api="setUnroll()">
    <div class="ticks" id="ticks"></div>
    <div class="track"><i id="fill"></i></div>
    <div class="knob" id="knob"></div>
    <div class="ends mono dim"><span><span class="long">ROLLED · OUTERMOST w059</span><span class="short">OUTER w059</span></span><span><span class="long">INNERMOST w023 · UNROLLED</span><span class="short">INNER w023</span></span></div>
  </div>
</footer>

<div class="credit mono dim">Data <a href="https://scrollprize.org/data_browser/PHerc0139" target="_blank" rel="noopener">Vesuvius Challenge</a> · <a href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank" rel="noopener">CC BY‑NC 4.0</a></div>

<div id="tip" class="tip mono" hidden></div>

<section id="focus" class="sheet" role="dialog" aria-modal="false" aria-labelledby="fTitle" hidden data-testid="closeup-panel">
  <div class="sheet-head">
    <div>
      <div class="mono dim" id="fKicker">WINDING</div>
      <h2 id="fTitle">w046</h2>
    </div>
    <div class="sheet-tools">
      <div class="seg small" id="fSeg" role="tablist" aria-label="Close-up view"></div>
      <button class="icon" id="fClose" data-testid="closeup-close" data-api="focus(null)" aria-label="Close">✕</button>
    </div>
  </div>
  <div class="viewer" id="viewer" tabindex="0" role="img" aria-label="Close-up image. Plus and minus zoom, arrows pan, 0 fits. Drag or pinch also work." data-testid="closeup-image" data-api="closeup()"><img id="fImg" alt="Ink detection of this winding, flattened" draggable="false"><img id="fImg2" class="cmp" alt="The comparison layer for the same winding" draggable="false" hidden>
    <div class="swipe-line" id="fSwipeLine" hidden></div>
    <div class="scalebar mono" id="fScale" aria-hidden="true"><i></i><span>10 mm</span></div>
    <div class="img-err mono dim" id="fErr" hidden>This image could not be loaded.</div>
    <label class="swipe mono" id="fSwipeWrap" hidden data-testid="compare-slider-label" data-api="compare(true, pos)"><span id="fSwipeA">Published ink (2.4 <span class="mu">µ</span>m)</span> <input type="range" id="fSwipe" data-testid="compare-slider" data-api="compare(true, pos)" min="0" max="100" value="50" aria-label="Swipe between Published ink (2.4 µm) and ink_9um (9 µm)"> <span id="fSwipeB">ink_9um (9 <span class="mu">µ</span>m)</span></label>
  </div>
  <div class="sheet-foot mono dim" id="fMeta"></div>
</section>

<section id="openSheet" class="sheet about open-own" role="dialog" aria-modal="false" aria-labelledby="oTitle" hidden data-testid="open-panel">
  <div class="sheet-head">
    <div><div class="mono dim">OPEN YOUR OWN</div><h2 id="oTitle">Unroll your own scroll</h2></div>
    <button class="icon" id="oClose" data-testid="open-close" data-api="panel('open', false)" aria-label="Close">✕</button>
  </div>
  <div class="about-body">
    <p class="o-private"><b>Nothing is uploaded.</b> Your files are read by this page inside your browser and kept on this device (in browser storage) until you remove them. No server sees them.</p>
    <div class="o-drop" id="oDrop" tabindex="0" role="button" data-testid="open-drop" data-api="open.fromFiles()" aria-label="Drop files here or press Enter to pick files">
      <div class="o-drop-text">Drop a <b>zip</b>, or a <b>manifest.json</b> with its meshes and images, or a folder</div>
      <div class="o-drop-btns">
        <label class="chip" data-testid="open-pick-files-label" data-api="open.fromFiles()">Pick files<input type="file" id="oPick" aria-label="Pick files" data-testid="open-pick-files" data-api="open.fromFiles()" multiple accept=".zip,.json,.obj,.ply,.png,.jpg,.jpeg,.tif,.tiff" hidden></label>
        <label class="chip" data-testid="open-pick-folder-label" data-api="open.fromFiles()">Pick a folder<input type="file" id="oPickDir" aria-label="Pick a folder" data-testid="open-pick-folder" data-api="open.fromFiles()" webkitdirectory multiple hidden></label>
      </div>
    </div>
    <form class="o-url" id="oURLForm"><input type="url" id="oURL" placeholder="https://example.org/my-scroll/manifest.json or .zip" aria-label="URL of a manifest or zip" data-testid="open-url" data-api="open.fromURL()"><button class="chip" type="submit" data-testid="open-url-submit" data-api="open.fromURL()">Open URL</button></form>
    <p class="mono dim small">A URL is fetched by your browser straight from that server, which must allow cross-origin requests (CORS).</p>
    <div class="o-status mono" id="oStatus" role="status" aria-live="polite" hidden></div>
    <div id="oSaved" hidden><div class="mono dim">IN THIS BROWSER</div><ul class="o-list" id="oSavedList"></ul></div>
    <p>Meshes: OBJ or PLY with texture coordinates (UVs), one per winding. Layers: PNG, JPG or TIFF images laid out on those UVs: an X-ray layer, an ink layer and an optional comparison layer. The manifest says which file is which, the voxel size, and who made the data.</p>
    <p><button class="chip" id="oExample" data-testid="open-example" data-api="open.example()">Download example package</button> <a href="https://huggingface.co/spaces/SharpMeow/unroll/blob/main/docs/ADDING_A_SCROLL.md#open-your-own" target="_blank" rel="noopener">How to make one</a></p>
    <p class="mono dim small" id="oLimits"></p>
    <p><a href="live.html" data-testid="open-live">Live sheet</a>: stream any public traced segment and a scan volume (CT or a 3D ink prediction) from the open data bucket and see the volume on the sheet in 3D, with nothing packed ahead of time.</p>
  </div>
</section>

<section id="about" class="sheet about" role="dialog" aria-modal="false" aria-labelledby="aTitle" hidden data-testid="about-panel">
  <div class="sheet-head">
    <div><div class="mono dim">ABOUT</div><h2 id="aTitle">What you&rsquo;re looking at</h2></div>
    <button class="icon" id="aClose" data-testid="about-close" data-api="panel('about', false)" aria-label="Close">✕</button>
  </div>
  <div class="about-body">
    <p>PHerc. 139 is one of the papyrus rolls carbonized when Vesuvius buried Herculaneum in 79 CE. It is too fragile to open, so it was scanned with X‑rays at the ESRF synchrotron in Grenoble. It is held by the Biblioteca Nazionale di Napoli.</p>
    <p>Each sheet in the 3D view is one winding of the roll, traced through the CT volume by the <a href="https://scrollprize.org" target="_blank" rel="noopener">Vesuvius Challenge</a>. The shapes are the real ones, from the public 9.362 µm meshes. The <b>X‑ray</b> layer is the CT intensity on each surface. The <b>Published ink (2.4 µm)</b> layer is the Vesuvius Challenge's public ink detection on the 2.4 µm scan. In 3D it is drawn through a local‑contrast filter so letter‑sized strokes stand out; the close‑up view shows the published preview unfiltered, downscaled to 2,400 px.</p>
    <p id="aboutGpu" hidden></p>
    <p>Unrolling lays the windings end to end, outermost first, the way a reader would open the roll. Each traced winding runs a little past one full turn, so roughly a tenth of the 2.8 m strip repeats surface from its neighbor.</p>
    <p class="mono dim small">Sources: open data bucket <code>PHerc0139/segments/*/mesh/*9.362um.tifxyz</code>, <code>surface-volumes/9.362um…zarr</code> and <code>ink-detection/downsampled</code>. Data: Giorgio Angelotti, Stephen Parsons, Sean Johnson, Elian Rafael Dal Prà, Johannes Rudolph, Paul Tafforeau, Alessandro Mirone, Paul Henderson, Hendrik Schilling, Forrest McDonald, David Josey, Youssef Nader, C. Seth Parker, W. Brent Seales. <i>Vesuvius Challenge - CT Scans of Herculaneum Papyri.</i> Vesuvius Challenge. Licensed <a href="https://creativecommons.org/licenses/by-nc/4.0/" target="_blank" rel="noopener">CC BY‑NC 4.0</a>. The page's code (HTML, CSS, JS and <code>tools/</code>) is Apache 2.0; see the Space's README.</p>
    <p class="mono dim small made-by">Made by Chase Hendrick.</p>
  </div>
  <div class="sheet-foot mono dim small about-foot"><span><a href="#connect" onclick="document.getElementById('btnEmbed').click();setTimeout(()=>document.getElementById('connect').scrollIntoView({behavior:'smooth'}),50);return false">Connect your tool</a> · For agents: <a href="llms.txt" target="_blank" rel="noopener">llms.txt</a> · <a href="AGENTS.md" target="_blank" rel="noopener">AGENTS.md</a> · <a href="api.json" target="_blank" rel="noopener">api.json</a></span> <span id="aVersion" class="version" data-testid="version" title="Version and build (CHANGELOG.md)"></span></div>
</section>

<section id="embedSheet" class="sheet about" role="dialog" aria-modal="false" aria-labelledby="eTitle" hidden data-testid="embed-panel">
  <div class="sheet-head">
    <div><div class="mono dim">EMBED</div><h2 id="eTitle">Put this viewer on your site, or its data in your tool</h2></div>
    <button class="icon" id="eClose" data-testid="embed-close" data-api="panel('embed', false)" aria-label="Close">✕</button>
  </div>
  <div class="about-body">
    <p>Paste this into your page. Visitors can unroll, scrub, switch layers and use every tool inside the frame; a click hands the mouse wheel to the viewer, so your page still scrolls normally.</p>
    <label class="mono small" data-testid="embed-option-state-label" data-api="embed.snippet({ state })"><input type="checkbox" id="embedState" checked data-testid="embed-option-state" data-api="embed.snippet({ state })"> Start at the current view</label>
    <label class="mono small" data-testid="embed-option-autoplay-label" data-api="embed.snippet({ autoplay })"><input type="checkbox" id="embedAutoplay" data-testid="embed-option-autoplay" data-api="embed.snippet({ autoplay })"> Autoplay the unroll</label>
    <label class="mono small" data-testid="embed-option-nocontrols-label" data-api="embed.snippet({ controls })"><input type="checkbox" id="embedNoControls" data-testid="embed-option-nocontrols" data-api="embed.snippet({ controls })"> Hide the controls</label>
    <textarea id="embedCode" class="embed-code mono" rows="5" readonly aria-label="Embed code" data-testid="embed-code" data-api="embed.snippet()"></textarea>
    <p><button class="chip" id="embedCopy" data-testid="embed-copy" data-api="embed.snippet()">Copy</button> <a id="embedPreviewURL" href="./" target="_blank" rel="noopener" data-testid="embed-preview" data-api="embed.snippet()">Preview the embed</a></p>
    <p class="mono dim small">oEmbed: <a href="oembed.json" target="_blank" rel="noopener">/oembed.json</a>. Host pages can drive the viewer with postMessage; see <a href="docs/EMBED.md" target="_blank" rel="noopener">EMBED.md</a>.</p>
    <!-- space-connect:start -->
<section class="scc" id="connect" aria-label="Connect your tool"><style>.scc{--a:var(--scc-accent,#4ea8de);color:inherit;font:inherit;line-height:1.5;max-width:60rem}
.scc h2{font-size:1.15em;margin:.2em 0 .3em}.scc h3{font-size:1em;margin:0 0 .2em}
.scc p{margin:.3em 0}.scc a{color:var(--a)}
.scc .scc-src{border:1px solid color-mix(in srgb,currentColor 18%,transparent);border-radius:10px;padding:.7em .9em;margin:.7em 0;background:color-mix(in srgb,currentColor 4%,transparent)}
.scc .scc-meta{font-size:.85em;opacity:.8}
.scc details{border-top:1px solid color-mix(in srgb,currentColor 12%,transparent);padding:.35em 0}
.scc summary{cursor:pointer;font-weight:600;list-style-position:inside}
.scc .scc-b{font-weight:400;font-size:.75em;padding:.05em .5em;border-radius:99px;margin-left:.5em;border:1px solid currentColor;opacity:.75;white-space:nowrap}
.scc .scc-b.ok{color:#3fb37f}.scc .scc-b.src{color:var(--a)}.scc .scc-b.warn{color:#e0a030}
.scc .scc-code{position:relative;margin:.4em 0 .2em}
.scc pre{margin:0;padding:.7em .8em;border-radius:8px;overflow:auto;font-size:.82em;background:color-mix(in srgb,currentColor 9%,transparent);white-space:pre}
.scc code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.scc button.scc-copy{all:unset;box-sizing:border-box;position:absolute;top:.35em;right:.35em;font-size:.75em;line-height:1.4;padding:.1em .6em;border-radius:6px;border:1px solid color-mix(in srgb,currentColor 30%,transparent);background:color-mix(in srgb,currentColor 8%,transparent);color:inherit;cursor:pointer}
.scc button.scc-copy:hover{border-color:var(--a)}
.scc .scc-url{position:relative}.scc .scc-url pre{padding-right:4.5em;white-space:pre-wrap;word-break:break-all}
.scc .scc-go{display:inline-block;margin:.3em 0;padding:.25em .8em;border-radius:6px;border:1px solid var(--a);text-decoration:none}
.scc footer{font-size:.8em;opacity:.75;margin-top:.6em}</style>
<h2>Connect your tool</h2>
<p>The traced windings, X-ray CT and ink layers in Unroll come from the Vesuvius Challenge open data, and every one of them reads straight from these URLs in your own tool, with no account and no download step.</p>
<div class="scc-src" id="connect-segment"><h3>A traced winding (tifxyz), example PHerc0841 w00</h3>
<p class="scc-meta">CC BY-NC 4.0 (Vesuvius Challenge open data) · Every winding Unroll shows has a folder like this under SCROLL/segments/ in the open data; Unroll keeps only a decimated copy for the browser.</p>
<div class="scc-url"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0841/segments/20260220213127-w00/mesh/20260220213127-on-20250821151531-9.366um.tifxyz</code></pre></div>
<details><summary>Python (numpy, tifffile)<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code># pip install numpy tifffile
import io, json, urllib.request
import numpy as np, tifffile

T = &quot;https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0841/segments/20260220213127-w00/mesh/20260220213127-on-20250821151531-9.366um.tifxyz&quot;
meta = json.load(urllib.request.urlopen(T + &quot;/meta.json&quot;))  # scale, bbox, format
get = lambda c: tifffile.imread(io.BytesIO(urllib.request.urlopen(f&quot;{T}/{c}.tif&quot;).read()))
xyz = np.stack([get(c) for c in &quot;xyz&quot;], -1)  # xyz[i, j] = (x, y, z) in scan voxels; -1 = no surface</code></pre></div></details>
</div>
<div class="scc-src" id="connect-segments"><h3>Every segment of a scroll, example PHerc0841</h3>
<p class="scc-meta">CC BY-NC 4.0 (Vesuvius Challenge open data) · Each segment folder has mesh/ (tifxyz), surface-volumes/ (OME-Zarr layers) and, where published, ink-detection/. · <a href="https://scrollprize.org/data_browser/PHerc0841" target="_blank" rel="noopener">browse</a></p>
<div class="scc-url"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>s3://vesuvius-challenge-open-data/PHerc0841/segments/</code></pre></div>
<details><summary>Python (list the folder)<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>import re, urllib.request

B = &quot;https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com&quot;
xml = urllib.request.urlopen(B + &quot;/?list-type=2&amp;delimiter=/&amp;prefix=PHerc0841/segments/&quot;).read().decode()
print(re.findall(r&quot;&lt;Prefix&gt;([^&lt;]+)&lt;/Prefix&gt;&quot;, xml))  # subfolders; &lt;Key&gt; gives files</code></pre></div></details>
</div>
<div class="scc-src" id="connect-ct"><h3>X-ray CT behind the CT view, PHerc0139 9.362 um</h3>
<p class="scc-meta">CC BY-NC 4.0 (Vesuvius Challenge open data) · Streamed from the open-data bucket, never copied here. Unroll opens any other scan with ?ctzarr=URL&amp;ctum=VOXEL_UM. · <a href="https://scrollprize.org/data_browser/PHerc0139" target="_blank" rel="noopener">browse</a></p>
<div class="scc-url"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0139/volumes/20250728140407-9.362um-1.2m-113keV-masked.zarr</code></pre></div>
<details><summary>Python (zarr)<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code># pip install &quot;zarr&gt;=3&quot; fsspec aiohttp
import zarr

root = zarr.open_group(&quot;https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0139/volumes/20250728140407-9.362um-1.2m-113keV-masked.zarr&quot;, mode=&quot;r&quot;)  # OME-Zarr, zarr v2
a = root[&quot;2&quot;]  # level 0 is full resolution; level L is 2**L times coarser
print(a.shape, a.dtype, a.chunks)
block = a[2622:2686, 828:892, 828:892]  # (z, y, x); reads only the chunks it touches</code></pre></div></details>
<details><summary>villa&#x27;s vesuvius package<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code># pip install &quot;vesuvius[volume-only]&quot;
from vesuvius import Volume

v = Volume(type=&quot;zarr&quot;, path=&quot;https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0139/volumes/20250728140407-9.362um-1.2m-113keV-masked.zarr/2&quot;)  # one pyramid level
block = v[2622:2686, 828:892, 828:892]</code></pre></div></details>
<details><summary>VC3D<span class="scc-b src">from villa&#x27;s source</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>In VC3D (villa&#x27;s volume-cartographer), open or create a project, then
File &gt; Attach Remote Zarr... and paste:

https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0139/volumes/20250728140407-9.362um-1.2m-113keV-masked.zarr

It accepts http, https and s3 OME-Zarr roots.</code></pre></div></details>
<details><summary>neuroglancer<span class="scc-b ok">tested 2026-10-24</span></summary>
<a class="scc-go" href="https://neuroglancer-demo.appspot.com/#!%7B%22layers%22%3A%5B%7B%22type%22%3A%22image%22%2C%22source%22%3A%22zarr%3A%2F%2Fhttps%3A%2F%2Fvesuvius-challenge-open-data.s3.us-east-1.amazonaws.com%2FPHerc0139%2Fvolumes%2F20250728140407-9.362um-1.2m-113keV-masked.zarr%2F%22%2C%22name%22%3A%22X-ray%20CT%20behind%20the%20CT%20view%2C%20PHerc0139%209.362%20um%22%7D%5D%2C%22layout%22%3A%224panel%22%7D" target="_blank" rel="noopener">Open in neuroglancer</a>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>https://neuroglancer-demo.appspot.com/#!%7B%22layers%22%3A%5B%7B%22type%22%3A%22image%22%2C%22source%22%3A%22zarr%3A%2F%2Fhttps%3A%2F%2Fvesuvius-challenge-open-data.s3.us-east-1.amazonaws.com%2FPHerc0139%2Fvolumes%2F20250728140407-9.362um-1.2m-113keV-masked.zarr%2F%22%2C%22name%22%3A%22X-ray%20CT%20behind%20the%20CT%20view%2C%20PHerc0139%209.362%20um%22%7D%5D%2C%22layout%22%3A%224panel%22%7D</code></pre></div></details>
<details><summary>curl<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>curl -s https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0139/volumes/20250728140407-9.362um-1.2m-113keV-masked.zarr/.zattrs            # OME multiscales metadata
curl -s https://vesuvius-challenge-open-data.s3.us-east-1.amazonaws.com/PHerc0139/volumes/20250728140407-9.362um-1.2m-113keV-masked.zarr/2/.zarray     # one level: shape, chunks, dtype, codec</code></pre></div></details>
</div>
<div class="scc-src" id="connect-page"><h3>Unroll&#x27;s own API, files and embed</h3>
<p class="scc-meta">Page code Apache 2.0; derived data CC BY-NC 4.0 · Every button has a data-api name and the same call on window.unroll, so a script or an agent can drive the page.</p>
<div class="scc-url"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>https://sharpmeow-unroll.static.hf.space/</code></pre></div>
<details><summary>Links and embed<span class="scc-b ok">tested 2026-10-24</span></summary>
<div class="scc-code"><button type="button" class="scc-copy" data-testid="connect-copy" onclick="navigator.clipboard&&navigator.clipboard.writeText(this.parentNode.querySelector('code').innerText).then(()=>{this.textContent='Copied';setTimeout(()=>this.textContent='Copy',1200)})">Copy</button><pre><code>API description (window.unroll, postMessage, deep links): https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/api.json
Guide for agents: https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/llms.txt
Scroll list and per-scroll manifests: https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/scrolls/index.json
Ink Bench on the command line: https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/tools/inkbench.py
Deep link to one winding: https://sharpmeow-unroll.static.hf.space/?scroll=PHerc0139&amp;t=0.6&amp;layer=overlay&amp;focus=w047&amp;compare=0.3

&lt;iframe src=&quot;https://sharpmeow-unroll.static.hf.space/index.html?embed=1&quot; width=&quot;100%&quot; height=&quot;600&quot; allow=&quot;fullscreen; clipboard-write&quot;&gt;&lt;/iframe&gt;</code></pre></div></details>
</div>
<footer>Machine-readable: <a href="https://huggingface.co/spaces/SharpMeow/unroll/blob/main/connect.json" target="_blank" rel="noopener">connect.json</a> and <a href="https://huggingface.co/spaces/SharpMeow/unroll/blob/main/CONNECT.md" target="_blank" rel="noopener">CONNECT.md</a>. Checked 2026-10-24. "Tested" means the snippet ran against this public data on that date. Scan data keeps its own license (the Vesuvius Challenge open data is CC BY-NC 4.0).</footer></section>
<!-- space-connect:end -->
  </div>
</section>

<script type="module" src="app.js?v=20261027one1"></script>
<script type="module" src="polish.js?v=20261027one1"></script>
<script type="module" src="lib/bench.js?v=20261027one1"></script>
<script type="module" src="ui.js?v=20261028phone1"></script>
<script type="module" src="lib/ctvolume.js?v=20261027one1"></script>
<script type="module" src="lib/ctwl.js?v=20261027one1"></script>
<script type="module" src="lib/dragres.js?v=20261027one1"></script>
<script type="module" src="lib/ctlabels.js?v=20261027one1"></script>
<script type="module" src="lib/ctapi.js?v=20261027one1"></script>
</body>
</html>