Analysis output¶
When you click Analyse, forgegen runs a multi-stage pipeline that builds a layered understanding of your media. The visible result is a .funscript — but the full analysis is preserved in a companion <stem>.analysis.json sidecar so downstream tools don't have to re-derive any of it.
This page explains what forgegen considers and what it writes.
What forgegen analyses¶
Layer 1 — audio features¶
- Beat grid — every beat detected by
librosa'sbeat_track(fast, deterministic) orplp(predominant local pulse — robust on long or genre-bending tracks). The detector is auto-selected based on track length. - BPM — derived from the beat grid.
- Downbeats — every 4th beat (v0.1 assumes 4/4 time).
- Per-beat energy — RMS energy at each beat, normalised 0–1. Peaks tend to land on kicks and snares.
- Phrases — every 16 beats grouped into one phrase (4 bars × 4/4). The unit forgegen uses for mode classification.
Layer 2 — structural inferences¶
- Phrase mode — each phrase is auto-classified as one of
break,tease,slow,steady,fast, oredgingbased on energy profile and BPM context. See Phrase Modes. - Energy trend per phrase — rising, falling, flat, or arc-shaped.
-
Chapters — read from:
- Embedded mp4 chapters (via
ffprobe -show_chapters) - A
<stem>.chapters.jsonsidecar next to the source
Audio auto-detection from silences/structural similarity is v0.5 scope and not done in v0.1. If neither source has chapters, the chapter list is simply empty.
- Embedded mp4 chapters (via
Layer 4 — generation decisions¶
When you pick a style and click Generate, forgegen records the choices it made:
- Style — Rhythmic / Sensual / Intense / Chaotic (sets
low,high, beat source) - Stroke density — 1, 2, 4, or 8 strokes per beat
- Tone trajectory — flat / rise / fall / auto
- Energy normalization — the 95th-percentile energy used to scale peak amplitude consistently across tracks
Layer 3 (event proposals) — auto-detected accents, edge holds, climax candidates — is v0.5+ scope. v0.1 emits an empty proposal list.
What gets exported¶
<stem>.funscript¶
Standard funscript JSON. Just the curve — actions: [{at, pos}, ...]. Nothing forge-specific.
<stem>.analysis.json (companion)¶
Written next to the funscript when you click Save to folder (browser download path is funscript-only). Minimal in v0.1:
{
"version": "1.0",
"generated_by": {
"tool": "forgegen",
"tool_version": "0.1.0-alpha",
"videoflow_version": "0.0.5-alpha",
"generated_at": "2026-04-29T19:25:02Z"
},
"source": {
"audio_path": "track.mp4",
"duration_ms": 279777,
"audio_md5": "8072d2881c179a94b4cef99ba3b834e1"
},
"structural": {
"chapter_proposals": [
// empty when source has no embedded chapters and no .chapters.json sidecar
]
}
}
The audio_md5 is a partial hash (first + last 1MB) — sufficient for downstream cache invalidation, cheap on long files.
What's coming in future versions¶
The schema is forward-compatible. Later releases will additively populate:
audio_features— bpm, beats, energy, phrases (so FunScriptForge skips re-analysis)video_features— scene changes, motion energy (when video path lands in v0.3)event_proposals— accents, edge holds, climax candidates (v0.5+ when forgevents arrives)generation_choices— exactly which style/density/tone produced this curve
Tools that read v1.0 today keep working: missing optional sections are simply ignored.
The full schema lives in analysis-schema.md.
Why the sidecar exists¶
The principle is canonical-emit — every analysis-derived value lives in one place and every downstream tool reads it instead of recomputing.
What that buys you in practice:
- FunScriptForge opens a funscript and finds the same chapters forgegen used — no re-analysis pass.
- ForgePlayer can show those chapters as visual markers during playback.
- forgevents (planned) consumes auto-detected event proposals as starting candidates for human curation.
- Future ML uses paired
<funscript, analysis.json>as labeled training data.
See canonical-emit-pattern.md for the full architectural rationale and how it applies across the forge tool family.