OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-social commits / 0b961be

docs/social-model: what we actually use to downsample, and three corrections
Images: browser canvas via resize-plan.js, client-side, already emitting one level - N is a small change at zero binary cost, and the WebView shell means the handset gets it too. Video: hypernote shells out to a SYSTEM ffmpeg binary, no app of ours depends on bare-ffmpeg, platform encoders exist, and the phone may not need to transcode at all. Corrects 6.6.4, which claimed images were the unsolved case without checking. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0b961be95243e055b34b0994386113b25df7ca0e
by plankatron <fleeky@gmail.com> · 2026-09-05T22:34:31.000Z

Changed files (1) +43 -4

docs/SOCIAL-MODEL.md +43 -4
……@@ -265,10 +265,14 @@ substrate; what is missing is generalising it to images and naming it as the mod
265265This is the constraint that decides whether the pyramid is clean or partial.
266266
267267hypernote generates its ladder with **ffmpeg via `bare-subprocess`, on a server**
268(`lib/media/video-processor.js`). A handset posting a photo would have to produce four levels at post
269time: CPU, battery, and an image encoder that may not exist in Bare on that device. **Video is
270already solved — HLS *is* the manifest. Images are not, and the phone is where most posts come
271from.**
268(`lib/media/video-processor.js:1,19-20,28`). A handset producing levels at post time costs CPU and
269battery, and needs an encoder that is actually present on the device.
270
271⚠ **Corrected 2026-09-05 — see §6.6.5.** An earlier version of this paragraph said images were the
272unsolved case because Bare might lack an image encoder on a handset. That was written without
273checking: images are downsampled in the **browser**, not in Bare, and `favela-android` is a WebView
274shell — so the encoder is almost certainly already there. Video is the case that needs a real
275decision.
272276
273277So the pyramid must be allowed to be **partial**, and that has three consequences:
274278
……@@ -284,6 +288,41 @@ So the pyramid must be allowed to be **partial**, and that has three consequence
284288worse a floor than what we ship now, and improves monotonically as encoders become available. That
285289is the property that makes it safe to adopt before the image encoder question is settled.
286290
291### 6.6.5 What we actually use today — measured, not assumed
292
293| | tool | where it runs |
294|---|---|---|
295| **image** | browser **`<canvas>`** — `createImageBitmap` → `drawImage` → `toBlob`, driven by `resize-plan.js` (`MAX_BYTES`, `resizePlan`, `aspectRatioOf`) | **client-side**, at compose time (`favela-web/web/static/compose.js:12,29-35`) |
296| **video** | the **`ffmpeg` / `ffprobe` system binaries** via `spawn` from `bare-subprocess` | **server-side** (`hypernote/lib/media/video-processor.js`) |
297
298**Images are close to free, and the ladder is half-built already.** `resizePlan` is a level-decision
299function that currently emits **one** level. Emitting N is a small change to existing code, costs no
300binary size because canvas is in the runtime, and — since `favela-android` is a WebView shell
301(hyper-social #45 decision 1) — the handset gets the same path. ⚠ Unverified against the actual
302WebView; verify before relying on it.
303
304**Video is the case that needs a decision, and three earlier claims of mine were wrong:**
305
3061. **No app of ours depends on `bare-ffmpeg`.** It is a transitive dependency of `keet-cli` only.
307 hypernote has **no ffmpeg dependency at all** — it shells out to a **system binary**
308 (`_ffmpegPath = opts.ffmpegPath || 'ffmpeg'`), which is exactly why it works on a server and
309 cannot work on a phone.
3102. **"No alternative to ffmpeg" was false.** Platform video encoders exist on both handsets — Android
311 `MediaCodec`, iOS VideoToolbox — reachable through the capability bridge that hyper-social #45
312 already chose as the direction.
3133. **The phone may not need to transcode video at all.** §6.6.4's partial pyramid lets it post one
314 level and leave the ladder to a bridge or a well-resourced peer. **Playback needs nothing either**
315 — the WebView plays HLS natively.
316
317**`bare-ffmpeg` 1.1.0**, if we do want on-device transcode, is a Bare **native addon** rather than a
318subprocess, which is what makes it viable where `spawn` is not. It ships prebuilds for
319`android-arm`/`arm64`/`ia32`/`x64` and `ios-arm64`. **Measured: `prebuilds/android-arm64/bare-ffmpeg.bare`
320is 29 MB** (linux-x64 is 39 MB; all 13 platforms together are 396 MB, but pgh's `by-arch/` publishing
321prunes to the target). Against an APK that hyper-social #39 fought from 527 MB down to 117 MB, that is
322roughly a 25% increase.
323
324⇒ **It is a capability purchase, not a requirement.** Buy it where the ladder is actually produced.
325
287326⛔ **Who derived a level still matters.** §8's *"never re-encode a record you did not author"* cost two
288327of wj62's posts. Levels generated at authoring are the author's; a level a bridge generated later is
289328derived, and the manifest must say so rather than presenting it as authored bytes.