Skip to content

Releases: Eggmansworld/EggmansLaserForge

v0.1.21 — The timeline shows the whole video, used and unused

Choose a tag to compare

@Eggmansworld Eggmansworld released this 24 Aug 20:15

New in 0.1.21

The strip above the transport slider used to draw the selected scene and nothing else — one short band floating in a wide empty bar. The empty space meant nothing: it looked identical whether it held the rest of the game or footage no part of the game had ever touched.

It now draws the whole video, always full, in three rows.

What's on it 🎨

Levels across the top — each level's extent, alternating shades, numbered and named where the band has room.

Coverage in the middle, colour-coded by what each stretch is for:

🟦 Blue A gameplay scene — footage a level plays
🟦 Dark blue A level's intro clip
🟥 Red A death scene
🟪 Violet A framework video slot — attract, title, Game Over…
▫️ Pale violet A single-frame still slot
⬜ Dashed outline A scene you marked that nothing references
⬛ Dark Unused video — no part of the game touches these frames
image

That dashed one is deliberately its own category. "You marked this and never wired it in" is a different problem from "spare footage", and hollow-versus-filled reads without needing the colour at all.

Moves along the bottom. The selected scene's moves stand full height; every other move in the video is a shorter stub, so the shape of the whole game stays visible while you work on one scene. A move that breaks the spacing rules still turns red.

Through all three: a playhead, the In/Out brackets drawn where they actually fall, and click or drag anywhere to seek. The line above names whatever the playhead is over — level, scene, death, slot, or an unused run with its length — and the figure on the right is how much of the video the game actually plays.

Why the dark parts are the point 🔍

A long dark run late in a project is worth a look. It might be a scene you meant to mark and forgot. And when the game is finished, it is a measure of how much of the file is dead weight — a feature film cut down to a laserdisc game routinely leaves some of itself unused.

The arithmetic behind it lives in the project layer rather than in a paint method, because the same answer will drive a "blank the leftover video" tool later, and something that overwrites frames must not be reading its ranges off a control that was only ever meant to be looked at.

Two things the tests settled 🧮

Coverage is a union, not a sum. Scenes overlap — a death reused by two levels, a slot sitting inside a level's footage — and adding their lengths reports more used frames than the video has.

Spans are clamped per video, so a scene straddling a join can't draw off the end of the one you're looking at.

Also caught while testing: TotalFrames returned 1 for an empty map, because an inclusive range of 0..0 is one frame. "No video loaded" and "a one-frame video" are now properly distinct.

Verified on a real multi-video project

[Unnamed Project] — five videos on one continuous frame line, 187,141 frames end to end. Each maps its own frames in global numbers, the slot names come through, and the hh:mm:ss clock runs unbroken across the join at 02:05:08.


725 automated checks pass.

Install

Download the ZIP below, right-click → Extract All…, and run LaserForge.exe. Self-contained and portable — no installer, no .NET runtime needed.

SHA-256:

940344fb31625ba07de65bd190623c01fb842e4b324d61a27d0150bcf71a3e58  EggmansLaserForge-0.1.21-win-x64.zip

v0.1.20 — Relative frames become real ones, and HDR stops washing out

Choose a tag to compare

@Eggmansworld Eggmansworld released this 24 Aug 02:24

New in 0.1.20

Two frame-number fixes found importing Tron (1982), a colour fix found converting an HDR video, and a small clock.

Scripts that count from the level, not the disc 🎞️

Some scripts set RelativeFrames = true. That one line changes what every frame number in the script means:

Level[1] = {"Arrival", 9749, 9984, 5, 0, 0, -1}
move[1]  = {271, 286, UP, 23}     -- really frames 10020–10035

The framework adds each level's own start frame to sceneStart, sceneEnd and both frames of every move (setupFrames, main.singe:6392). LaserForge read those numbers as disc frames, so Tron's Level 1 imported with every scene sitting 9,749 frames early, and Level 9 was out by nearly 60,000.

It was worse than a display problem. The export writes RelativeFrames = false, so the framework then added nothing back and the game was unplayable.

Each level's start frame is now folded in on import, and the export pins RelativeFrames = false for real — it was only false by accident before, because the built-in template happened to say so. A template.singe copied from a relative-frame game would have carried the true through and had the framework add every base a second time.

Death[], Level[] and the menu slots are left alone: setupFrames never touches them, so they're already real frame numbers under both settings.

The proof it landed, from the real script:

level          introEnd   firstScene   gap
Arrival            9984        9985      1
Ball Game         15232       15233      1
Light Cycles      21164       21165      1
...
MCP               60718       60719      1

Every level's intro now ends exactly one frame before its first scene, all the way down the disc. That only lines up if every base is right.

The secret level was being eaten by the level above it 🕵️

Tron writes its bonus level as:

elseif thisLevel == levelSecret then     -- levelSecret = 1000

The importer's branch pattern only accepted digits, so it never matched — and currentLevel stayed on 9. Level 9 quietly adopted the secret level's four scenes, importing with eleven scenes against the script's seven. With the fix above they'd also have taken the wrong base: 18,355 frames out.

The branch is now recognised, and its scenes are reported rather than adopted. Levels export renumbered 1..N with finalstage and PlayOrder built from the same count, so importing it would have promoted the bonus level to a mandatory last stage of the story. It stays untouched in your original script.

HDR video no longer converts to grey mush 🎨

Converting a 4K HDR master produced a .m2v that was subdued, milky and nearly colourless. Here's why:

primaries transfer depth
source .mkv bt2020 smpte2084 (PQ) 10-bit
converted .m2v bt2020 smpte2084 8-bit

The conversion was only truncating 10-bit to 8-bit and copying the colour tags across. Every number in the file survived; every number meant something different. PQ spreads brightness over a 10,000-nit range, so ordinary picture content sits high on that curve — read back as plain gamma it lifts the whole image into a flat mid-grey. And BT.2020 colours read off the BT.709 triangle collapse toward neutral. The file was also mistagged, claiming to be HDR while holding SDR pixels.

Convert Video to M2V now spots this when it probes the file and pre-ticks Convert HDR to SDR for it. Measured on one frame, average saturation went 5.51 → 8.55.

Three deliberate choices in there:

  • hable as the curve — it holds highlights instead of clipping them, which matters for a mostly-dark film with small very bright sources.
  • desat=0 — tonemap's highlight desaturation defaults on and pulls bright areas toward white, the exact thing this exists to stop.
  • The downscale moved inside the chain, so the resize happens in linear light at full precision instead of afterwards in 8-bit.

The output is tagged BT.709 so nothing downstream tries to "fix" it again. It costs about 2.3× the encode time, which the dialog tells you. A BT.2020 source that isn't HDR gets a gamut-only conversion, and plain BT.709 sources are untouched — the option doesn't even appear for them.

An hh:mm:ss clock beside "Go to" ⏱️

Sometimes you just want to know how far into the film you are.

It follows everything — buttons, arrow keys, slider, jog, playback, Go to — and runs continuously across a multi-video project rather than resetting to zero at each file, so it climbs alongside the frame counter. When there's no frame rate to work from, or the frame is in a gap or past the end, it shows --:--:-- rather than making something up.

Frames are still the unit this app works in and always will be. The clock is rounded to the second and never feeds back into a frame number.


693 automated checks pass.

Install

Download the ZIP below, right-click → Extract All…, and run LaserForge.exe. Self-contained and portable — no installer, no .NET runtime needed.

SHA-256:

49a199221b069149e3940a55340c9e04e30c41b45215561a911f8e7e610a4736  EggmansLaserForge-0.1.20-win-x64.zip

v0.1.19 — Framework v3.32j and FrameworkKimmy v1.01h

Choose a tag to compare

@Eggmansworld Eggmansworld released this 23 Aug 02:11

New in 0.1.19

Updated frameworks, and a README correction.

Framework v3.32j and FrameworkKimmy v1.01h 🧩

Both bundled frameworks are refreshed to DirtBagXon's latest (upstream commit b300895, 16 Aug):

  • Pre-emptive support for a BILINEAR scale option in the Service Menu. It shows as N/A on current Hypseus releases — it's there ready for a future one. bScan / mycolor3 become bBilinear / dip_Bilinear, and the scanlines handling moves out of main.singe into toolbox.singe.
  • Two labelling fixes — an old bug in % mode (reported by Tagai), and H → X.

Nothing in gameplay or move handling changed; PATH, TIMED and the optional-move logic are byte-identical to the previous build.

These only reach you through a release, because a release is where the app gets the copies it installs into your Hypseus singe/ folder. FrameworkKimmy still ships with the DEFAULT skin only — upstream's archive carries nine skin folders and the other eight have never been wanted here.

Upstream now ships CRLF line endings. Everything under Frameworks/ and DefaultGameFiles/ is converted to LF before shipping, which matters beyond tidiness: the high-score and save files are parsed by slicing fields with string.sub, so a stray carriage return lands inside the last field and stops it matching the letter it should be. That was the crash behind 0.1.9, and this release is verified to ship 0 carriage returns across all of it.

README: what actually survives an import 📄

The moves section promised that branch constructs are "kept exactly as written" on import. That wasn't true until 0.1.18 — the move line survived, but the row describing what each answer does did not.

It now says what really happens, names PATH and TIMED alongside CHOOSE, and shows the two-line shape a branch move actually has:

move[1] = {7356, 7500, PATH, -1}          -- a decision happens here
path[1] = {BUTTON1,1039,0,0,0,0,0,0,2}    -- and this is the decision

Download & run

  1. Download EggmansLaserForge-0.1.19-win-x64.zip below.
  2. Right-click it → Extract All… into a folder of your choice (e.g. C:\LaserForge).
  3. Open the folder and run LaserForge.exe.

If Windows SmartScreen shows "Windows protected your PC", click More info → Run anyway — unsigned indie software, and only the first time.

Already have the frameworks installed? The app only drops them in when they're missing, so an existing singe/Framework folder is left alone. To take the update, replace the Framework and FrameworkKimmy folders in your Hypseus singe/ directory with the ones from this ZIP — and make sure they land directly there, not nested one level deeper.

One-time setup for video conversion (optional)

Download the full FFmpeg build — ffmpeg-git-full.7z — from https://www.gyan.dev/ffmpeg/builds/ (under "latest git master branch build"; the full build, not essentials). Extract it anywhere, and when LaserForge asks, point it at ffmpeg.exe inside the extracted bin\ folder. It's remembered after that.

Requirements

  • Windows 10 / 11, 64-bit, 1680x1050 minimum resolution
  • Hypseus Singe — the emulator that runs your game (separate free download; grab the latest Windows 64-bit release — v3.x SDL3 and v2.12.1 both work fine)
  • Your game's .m2v video — or a source video (mkv / mp4 / webm) to convert

Fully self-contained and portable: the .NET runtime, FFmpeg playback natives, both global Singe frameworks, and the default game support files are all inside the ZIP.

See the README for a complete, beginner-friendly walkthrough.


SHA-256 — EggmansLaserForge-0.1.19-win-x64.zip
4dcb121e5c2c0bb2b2e2d5859d1eb3a4799f924b7ee4c829e17b559c4bf9f2f7

v0.1.18 — Branch moves keep their table, and imports replace

Choose a tag to compare

@Eggmansworld Eggmansworld released this 23 Aug 00:50

New in 0.1.18

Two bugs that fed each other, found while importing a real community game.

A branch move is two lines 🔀

move[1] = {7356, 7500, PATH, -1}          -- a decision happens here
path[1] = {BUTTON1,1039,0,0,0,0,0,0,2}    -- and this is the decision

LaserForge only ever read the first one. The second was silently dropped.

That doesn't produce a game missing a branch — it produces one that crashes. The framework clears the table at the start of every scene:

path = nil; path = {}

so a PATH move with no row makes path[currentMove][1] index a nil value the moment the move plays.

It was never PATH-specific. timed[] is script-supplied and move-indexed too, so a TIMED move lost its row exactly the same way and failed identically.

Both rows are now kept and written back. They're carried on the move, so if you add or delete moves the row is renumbered along with it, rather than staying put and quietly coming to describe somebody else's move. And the exporter now refuses to ship a PATH or TIMED move whose row has gone missing, because that's the crash.

(For the curious: that row reads "press Button 1 → target 1039". The framework treats any target over 1000 as a death, so 1039 is Death[39]. Field 9 says which move to resume at afterwards. And yes — the standard Framework supports PATH, not just FrameworkKimmy; PATH = 51 is in both, and the standard one has the more complete implementation.)

Importing replaces the game instead of merging it ♻️

This is the one that made the fix above look like it hadn't worked.

Importing rebuilt the scenes from scratch but appended the levels. So importing the same script twice gave you a 26-level game whose L14–L26 duplicated L1–L13 — while your original levels went on pointing at the original scenes. The corrected scenes were sitting right there in the project; they just weren't the ones any level used, and the exported game was built from the stale half.

Deleting the duplicates didn't help either: the numbering came from a list that only ever grew, so the next import started at L27. One real project had reached 69 scenes, of which 13 were reachable.

An import now rebuilds levels, scenes, moves, deaths and slots outright. Import the same file as many times as you like:

first  : 13 levels, 68 scenes
again  : 13 levels, 68 scenes
third  : 13 levels, 68 scenes

Your videos are kept — they belong to the project, not the script. Because this is destructive, importing into a project that already holds something now asks first and names exactly what it will replace, and Ctrl+Z undoes the entire import.

If you imported a game before this release, import it again. It will collapse the duplicate scenes and put the branch tables into the levels that actually run.


Download & run

  1. Download EggmansLaserForge-0.1.18-win-x64.zip below.
  2. Right-click it → Extract All… into a folder of your choice (e.g. C:\LaserForge).
  3. Open the folder and run LaserForge.exe.

If Windows SmartScreen shows "Windows protected your PC", click More info → Run anyway — unsigned indie software, and only the first time.

One-time setup for video conversion (optional)

Download the full FFmpeg build — ffmpeg-git-full.7z — from https://www.gyan.dev/ffmpeg/builds/ (under "latest git master branch build"; the full build, not essentials). Extract it anywhere, and when LaserForge asks, point it at ffmpeg.exe inside the extracted bin\ folder. It's remembered after that.

Requirements

  • Windows 10 / 11, 64-bit, 1680x1050 minimum resolution
  • Hypseus Singe — the emulator that runs your game (separate free download; grab the latest Windows 64-bit release — v3.x SDL3 and v2.12.1 both work fine)
  • Your game's .m2v video — or a source video (mkv / mp4 / webm) to convert

Fully self-contained and portable: the .NET runtime, FFmpeg playback natives, both global Singe frameworks, and the default game support files are all inside the ZIP.

See the README for a complete, beginner-friendly walkthrough.


SHA-256 — EggmansLaserForge-0.1.18-win-x64.zip
891999d588cf724d96849820761388afa2c1e50fb1b9d4ab09fc0874751fb9d0

v0.1.17 — Reading the scripts that count backwards

Choose a tag to compare

@Eggmansworld Eggmansworld released this 22 Aug 22:48

New in 0.1.17

Some scripts count backwards, and LaserForge couldn't read them.

The import that dropped every move 🩹

A community script imported with all 45 of its moves gone and 48 warnings. Three separate problems, and the log's wording hid two of them behind the first.

1. The arithmetic was written the other way round.

LaserForge already understood frames counted off a landmark — frameVictory = offsetMenus +3. But that's name then number, and plenty of authors think of a move as a deadline instead, counting backwards from the frame you can actually see on screen:

gap = 10
move[1] = {5679-gap, 5679, BUTTON1, 2}

Number then name. Same arithmetic, mirrored — and nothing matched it, so 40 moves were reported "malformed" and skipped. Both operand orders now resolve, in move frames, scene bounds and the Death table alike.

That style is worth understanding if you meet it: the window ends on the cue rather than starting there. The move above opens at 5669 and closes at 5679.

2. -1 isn't a death.

The Death# field was read as an unsigned number, so two more moves were dropped for their sign. The framework reads negatives as something else entirely:

-1   optional move          -- miss it and play simply continues
-2   optional move with score

Those are beats a game was deliberately designed around, and deleting them on import is silent damage. They're now kept exactly as written and put straight back on export, and the move's death picker says "Optional — missing it costs nothing" rather than showing a death somebody might feel they should fill in.

3. The whole Death table was invisible.

Deaths are usually one run of footage counted off a landmark too — Death[01] = {offsetDeath+32, offsetDeath+181} — and that read digits only. An unmatched Death[] line looks exactly like a script that defines no deaths, so all 42 vanished, and every move that survived then reported the death it names as missing. Those 45 "missing Death[n]" warnings were one bug wearing 45 hats.

Result on that script: 45 moves and 42 deaths — matching its own totalDeath = 42 — with 4 informational notes left, and every move line round-tripping identically to the original.

If you already imported a game and its moves came in short, import it again. The project file is fine; it was the reading that lost them.

Death# 0 means random death 🎲

This one had been wrong since the feature shipped. LaserForge offered a per-move choice labelled "No death (the scene shows the failure)" — but the framework does this:

if move[currentMove][moveDeath] == 0 then   -- 0 = random death
    q = math.random(totalDeath)

So anyone picking "no death" was getting a random one from the pool — the opposite of what the app promised. The choice now reads "Random death — the framework picks one (Death# 0)", and the export warning tells you which of the two a bare 0 will actually be.

Nothing in your projects changes meaning: the value was always Death# 0, only the label lied. Projects saved before this still load exactly as they were.

(A Skip is genuinely different — it can't be failed, so its Death# is never read at all.)


Download & run

  1. Download EggmansLaserForge-0.1.17-win-x64.zip below.
  2. Right-click it → Extract All… into a folder of your choice (e.g. C:\LaserForge).
  3. Open the folder and run LaserForge.exe.

If Windows SmartScreen shows "Windows protected your PC", click More info → Run anyway — unsigned indie software, and only the first time.

One-time setup for video conversion (optional)

Download the full FFmpeg build — ffmpeg-git-full.7z — from https://www.gyan.dev/ffmpeg/builds/ (under "latest git master branch build"; the full build, not essentials). Extract it anywhere, and when LaserForge asks, point it at ffmpeg.exe inside the extracted bin\ folder. It's remembered after that.

Requirements

  • Windows 10 / 11, 64-bit, 1680x1050 minimum resolution
  • Hypseus Singe — the emulator that runs your game (separate free download; grab the latest Windows 64-bit release — v3.x SDL3 and v2.12.1 both work fine)
  • Your game's .m2v video — or a source video (mkv / mp4 / webm) to convert

Fully self-contained and portable: the .NET runtime, FFmpeg playback natives, both global Singe frameworks, and the default game support files are all inside the ZIP.

See the README for a complete, beginner-friendly walkthrough.


SHA-256 — EggmansLaserForge-0.1.17-win-x64.zip
983806eeaffc7f3837e74ce8315267c560dcc8e31f685fd55810c373f7b5e8fd

v0.1.16 — Import crash fixed, and two video repairs

Choose a tag to compare

@Eggmansworld Eggmansworld released this 02 Aug 04:30

New in 0.1.16

An import crash, and two tools for video you already have.

Importing a script no longer fails at the finish line 🩹

If an import had anything to say — a corrected level intro, frames written as arithmetic, a move this editor can't author — it died with:

Import failed: Object reference not set to an instance of an object

The import had already worked. The scenes, moves and slots were read and saved. What broke was the summary window 0.1.15 added to show you the notes, and it took the whole operation down with it, so a perfectly good import looked like a rejected one.

The cause was invisible: Avalonia writes the code that connects every named control to its field, and both dialogs added last release also declared a method of their own that quietly replaced it. It compiles clean and warns about nothing — the window just arrives with every control missing. Tools → Reset All Move Timings was broken the same way and would have crashed the first time anyone used it.

Both are fixed, and the test suite now fails if any dialog is ever written that way again.

Thumbnails appear when the video does 🖼

Two related problems, both reported as "I have to close and reopen the project":

  • Adding a video never went back for the scene pictures that were waiting on it.
  • Even when it did, scene rows are cached — and the cached row had been built when no picture existed, so it kept showing the empty one.

Importing a script into a fresh game folder is the normal way to start from an existing game, and that folder has no video yet. The import now says so plainly instead of leaving you with a storyboard of blank tiles, and adding the video fills everything in on the spot.

Black out frames ⬛

Tools → ⬛ Black Out Frames…

Some published games carry the whole feature film cut into out-of-order pieces, with the deaths, still frames and system videos tacked on the end. Once you have the film as its own clean video, the film half of the original is dead weight — but its frame numbers are not, because every death and still slot in the imported script points into that same file.

This paints a span black without moving a single frame. Pick from the start, up to frame K, or a range from A to B — both ends included, using the numbers on the app's own frame counter. It tells you how many frames that is before it runs.

Roughly 85% of the blanked span disappears. Black isn't free — the cost is per-frame overhead, not picture — so expect a big cut rather than nothing at all.

Before the app will use the result it re-reads it and checks the frame count matches the original exactly. If it doesn't, nothing is swapped and it says why: a frame gained or lost would move every scene, move, death and slot at once. Your original file stays on disk, and Ctrl+Z undoes the swap.

Change a clip's frame rate ⏩

Tools → ⏩ Change Frame Rate…

Every video in a game has to share one frame rate, because all move timing is counted in frames — so a 25 fps clip can't join a 29.97 fps project until it's re-timed. The target is preselected to your project's rate, and you see the frame count before and after before committing.

The clip's real rate is read out of the video itself. This matters more than it sounds: ffprobe reports 25 fps for a raw .m2v that is actually 29.97, and a guessed rate re-times by exactly the wrong ratio. The broadcast rates are also written as their true ratios rather than the decimals people say out loud, because 29.97 drifts a frame every couple of minutes.

It's a real re-timing, not a relabel, so the frame count changes — which is why it's for a clip you haven't added to the project yet.

Death frame ranges

The DEATHS table in Game Setup now shows each death's frame range next to its name, the same way the scenes list does.

When something does go wrong

A failure used to give you one line naming neither the operation nor the place it broke. Import and export failures now put the full detail in the log, where you can select and copy it.


Download & run

  1. Download EggmansLaserForge-0.1.16-win-x64.zip below.
  2. Right-click it → Extract All… into a folder of your choice (e.g. C:\LaserForge).
  3. Open the folder and run LaserForge.exe.

If Windows SmartScreen shows "Windows protected your PC", click More info → Run anyway — unsigned indie software, and only the first time.

One-time setup for video conversion (optional)

Download the full FFmpeg build — ffmpeg-git-full.7z — from https://www.gyan.dev/ffmpeg/builds/ (under "latest git master branch build"; the full build, not essentials). Extract it anywhere, and when LaserForge asks, point it at ffmpeg.exe inside the extracted bin\ folder. It's remembered after that.

The two new video tools use FFmpeg as well.

Requirements

  • Windows 10 / 11, 64-bit
  • Hypseus Singe — the emulator that runs your game (separate free download; grab the latest Windows 64-bit release — v3.x SDL3 and v2.12.1 both work fine)
  • Your game's .m2v video — or a source video (mkv / mp4 / webm) to convert

Fully self-contained and portable: the .NET runtime, FFmpeg playback natives, both global Singe frameworks, and the default game support files are all inside the ZIP.

See the README for a complete, beginner-friendly walkthrough.


SHA-256 — EggmansLaserForge-0.1.16-win-x64.zip
095c0920d62049fd58b643988729022bef86b22f7c7880456e9a2e328743d2ab

Eggman's LaserForge 0.1.15 — Holds, timings, and a log you can read

Choose a tag to compare

@Eggmansworld Eggmansworld released this 01 Aug 16:02

New in 0.1.15

Six fixes, all from actually building a game with the thing.

Holds stopped crying wolf ✋

A hold released on the very next frame — which is simply how you author one — was flagged as too close together:

005876–005916  Hold Up
005917         Let go        ⚠ (not any more)

A release isn't a second reaction. The player is already holding the input, so there's nothing to react to and no reason to wait. The cushion between a hold and its release is gone.

Everything after the release is still measured from it, so a real move crowding the end of a gesture is still caught — which was the only warning that made sense here.

Reset all move timings ⏱

Tools → Reset All Move Timings… puts every hand-set reaction window back to the standard one.

Some published games shorten each move's window as a difficulty knob. It looks like authoring, but it defeats the difficulty system the framework already has: Normal, Hard and Extreme each shrink the window further, so a window hand-cut to a handful of frames leaves nothing to shrink — those modes stop being playable rather than getting harder. Games built that way only really work on the one mode their author tested.

Skips keep their windows: a skip's window is the passage it covers, not a reaction time. The tool tells you how many moves it'll touch before doing anything, and Ctrl+Z puts them back.

Clear a run of moves in one go 🗑

The moves list takes Ctrl+click and Shift+click now, so you can pick out several — or a whole range — and delete them together instead of click, delete, click, delete.

Deleting a hold takes its release with it, since a stranded release would be read as belonging to whatever move ended up in front of it.

An import that has something to say now says it 📋

Imports can correct level intros that swallow gameplay, resolve frames written as arithmetic, and flag moves this editor can't author. Those notes went to the log — where they scrolled past behind the thumbnails still decoding.

There's a summary window now when an import has warnings, with a copy button. They're still in the log too.

The log is one block of text 📄

Selecting in the log could only ever cover one line, because each line was its own selectable block. It's a single run of text now, so you can drag across as many lines as you want and copy just the part you're interested in.

Matching fonts

The Two-input & hold moves… and Death pickers were a size larger than the Scenes sort dropdown. Both match now.


Download & run

  1. Download EggmansLaserForge-0.1.15-win-x64.zip below.
  2. Right-click it → Extract All… into a folder of your choice (e.g. C:\LaserForge).
  3. Open the folder and run LaserForge.exe.

If Windows SmartScreen shows "Windows protected your PC", click More info → Run anyway — unsigned indie software, and only the first time.

One-time setup for video conversion (optional)

Download the full FFmpeg build — ffmpeg-git-full.7z — from https://www.gyan.dev/ffmpeg/builds/ (under "latest git master branch build"; the full build, not essentials). Extract it anywhere, and when LaserForge asks, point it at ffmpeg.exe inside the extracted bin\ folder. It's remembered after that.

Requirements

  • Windows 10 / 11, 64-bit
  • Hypseus Singe — the emulator that runs your game (separate free download; grab the latest Windows 64-bit release — v3.x SDL3 and v2.12.1 both work fine)
  • Your game's .m2v video — or a source video (mkv / mp4 / webm) to convert

Fully self-contained and portable: the .NET runtime, FFmpeg playback natives, both global Singe frameworks, and the default game support files are all inside the ZIP.

See the README for a complete, beginner-friendly walkthrough.


SHA-256 — EggmansLaserForge-0.1.15-win-x64.zip
0db7ef790ef2f449df869c20f2e8685a1cb2b4d76aaf4470dd37fd407bbcc7a5

Eggman's LaserForge 0.1.14 — A death scene for every move

Choose a tag to compare

@Eggmansworld Eggmansworld released this 01 Aug 12:29

New in 0.1.14

Death scenes, per move.

A death scene for each move 💀

Until now a scene had one death, and every move in it used that. The game script has always allowed one per move — so a scene can have the player die differently depending on which beat they miss, and a game with a big library of death footage can finally use it.

Select a move, then pick from the Death box under the interaction buttons:

  • Default — whatever the scene would use anyway. It names the actual scene, so you can see what you're inheriting rather than guessing.
  • No death — the footage itself shows the failure and the game carries on.
  • Any death in the table, by its number and name.

The DEATHS section in Game Setup 📋

A new section listing the script's Death[] table, in the order the script numbers it, with thumbnails.

Wiring a scene to a storyboard Death port still adds it automatically — those show as auto. What the section adds is the two things a wire can't do: keeping a spare death that nothing points at yet, and fixing the order. Those numbers are what every move referencing a death points at, so being able to see and control them matters.

A default death for a whole level 🎚

Under a level's ▸ more row. Scenes in that level with no Death wire of their own now fall back to it, instead of you wiring the same death to every scene.

It's a fallback, not an override — a scene's own Death wire always wins, so nothing in your existing projects changes.

One fix worth mentioning 🔧

The Deaths filter chip in the scenes list has been broken since it shipped in 0.1.8. It matched only per-move deaths, and nothing could set one — so unless you'd imported a game that already had them, that chip showed you nothing.

"Death scene" was being worked out in three different places that quietly disagreed with each other. There's one answer now, shared by the exporter, the storyboard's DEATH chips and that filter, so all three finally agree on what the exported script will contain.


Download & run

  1. Download EggmansLaserForge-0.1.14-win-x64.zip below.
  2. Right-click it → Extract All… into a folder of your choice (e.g. C:\LaserForge).
  3. Open the folder and run LaserForge.exe.

If Windows SmartScreen shows "Windows protected your PC", click More info → Run anyway — unsigned indie software, and only the first time.

One-time setup for video conversion (optional)

Download the full FFmpeg build — ffmpeg-git-full.7z — from https://www.gyan.dev/ffmpeg/builds/ (under "latest git master branch build"; the full build, not essentials). Extract it anywhere, and when LaserForge asks, point it at ffmpeg.exe inside the extracted bin\ folder. It's remembered after that.

Requirements

  • Windows 10 / 11, 64-bit
  • Hypseus Singe — the emulator that runs your game (separate free download; grab the latest Windows 64-bit release — v3.x SDL3 and v2.12.1 both work fine)
  • Your game's .m2v video — or a source video (mkv / mp4 / webm) to convert

Fully self-contained and portable: the .NET runtime, FFmpeg playback natives, both global Singe frameworks, and the default game support files are all inside the ZIP.

See the README for a complete, beginner-friendly walkthrough.


SHA-256 — EggmansLaserForge-0.1.14-win-x64.zip
caa207f70a8a7b57fb09f1ba3c6d361ea6c83510fe98a3b264bc50635924ec4e

Eggman's LaserForge 0.1.13 — Two-input moves, and no more lost ones

Choose a tag to compare

@Eggmansworld Eggmansworld released this 01 Aug 11:46

New in 0.1.13

Support for the moves that take more than one input — and, more importantly, an end to losing moves this app couldn't name.

Importing no longer drops moves 🛟

The frameworks define a lot more than the six inputs this editor authors. Anything else failed to parse and the move was thrown away with a line in the log. The game still exported. It was just missing its hardest beats — silent data loss wearing a warning's clothes.

Nothing is dropped now. Every move type the frameworks define survives an import, and comes back out of an export saying exactly what it said going in — including the ones the editor still can't edit. A MASHMAX used to have exported as WAY, quietly turning a button-mash into "any direction."

If you've imported a game and something felt missing, re-import it.

Two-input and hold moves ✋

A new + Two-input & hold moves… picker under the interaction buttons:

Move Player does
Up + Left, Up + Right, Down + Left, Down + Right Holds two directions together
Button 1 + Up / Down / Left / Right Holds the action button and a direction
Hold Up / Down / Left / Right / Button 1 Holds an input, then releases on cue

None of these needs a gamepad. A diagonal is two direction keys pressed at the same time — the game engine tests them as two separate inputs being held, and takes those from whatever your controls are bound to. They're on a picker rather than the keyboard simply because the six basic inputs are the ones worth muscle memory.

Choosing a Hold adds two moves — the hold and a Let go after it — because the engine reads the move following a hold as its release. Jog the Let go to wherever the player should release. If a hold ends up without one, exporting now tells you the scene and the frame.

Move types that stay read-only

Mash rates (MASH, MASHMAX, RUN…) and branch constructs (CHOOSE, PATH, YESNO) aren't in the picker. They aren't single moves — a mash rate is one move with a difficulty setting, and CHOOSE spans several rows and rewrites part of itself while the game runs. Offering them as ordinary moves would produce scripts that don't work.

They import, export and round-trip untouched, and appear in the move list under their own script name.


Download & run

  1. Download EggmansLaserForge-0.1.13-win-x64.zip below.
  2. Right-click it → Extract All… into a folder of your choice (e.g. C:\LaserForge).
  3. Open the folder and run LaserForge.exe.

If Windows SmartScreen shows "Windows protected your PC", click More info → Run anyway — unsigned indie software, and only the first time.

One-time setup for video conversion (optional)

Download the full FFmpeg build — ffmpeg-git-full.7z — from https://www.gyan.dev/ffmpeg/builds/ (under "latest git master branch build"; the full build, not essentials). Extract it anywhere, and when LaserForge asks, point it at ffmpeg.exe inside the extracted bin\ folder. It's remembered after that.

Requirements

  • Windows 10 / 11, 64-bit
  • Hypseus Singe — the emulator that runs your game (separate free download; grab the latest Windows 64-bit release — v3.x SDL3 and v2.12.1 both work fine)
  • Your game's .m2v video — or a source video (mkv / mp4 / webm) to convert

Fully self-contained and portable: the .NET runtime, FFmpeg playback natives, both global Singe frameworks, and the default game support files are all inside the ZIP.

See the README for a complete, beginner-friendly walkthrough.


SHA-256 — EggmansLaserForge-0.1.13-win-x64.zip
46932aceaf8cf9fe9432a71c6d443315d1a6c7b63c6418f3a96cc2c75ba4fd87

Eggman's LaserForge 0.1.12 — Truer script imports

Choose a tag to compare

@Eggmansworld Eggmansworld released this 01 Aug 09:58

New in 0.1.12

Importing other people's games got a lot more honest. Three fixes, all in the same place: what LaserForge does with a .singe script it didn't write.

Frame numbers written as arithmetic 🔢

Plenty of authors never type an absolute frame for the menu block. They find one landmark and count from it:

offsetMenus   = 49666
frameOptions  = offsetMenus +0
frameVictory  = offsetMenus +3
frameRankings = offsetMenus +12

That's perfectly good Lua, and the importer used to require a plain number after the =. So every one of those lines was skipped — and a skipped line looks exactly like an absent one, which meant those slots imported as unset, with nothing said about it.

They now resolve to real frames. Values built on other derived values work, order doesn't matter, subtraction works, and scene bounds accept the same form. A name the script never actually defines is reported rather than guessed at.

Level intro clips that aren't intros 🎬

A level line carries two frames before its scene count — where the level starts, and where its intro clip ends. The framework plays that span once, before the level begins.

Scripts in the wild frequently put something else in that second slot: the end of the level, or where the first scene starts. The result is an "intro" thousands of frames long that plays a chunk of the game before the level does.

Level[2] = {"Yakuza",   19030, 21775, ...}   2745 frames
Level[3] = {"Ceremony", 26775, 34182, ...}   7407 frames

Those are now corrected on import and reported in the log, so you can see what was changed rather than opening the project and finding an intro passage you never wrote.

The test is structural rather than a guess at length: an intro plays before the gameplay, so it may not run past the level's first scene. A genuine title card passes at any length, and a short value that still swallows the opening scene is caught.

Scripts using RelativeFrames are left alone — there, the level's start frame is the base every scene is measured from, so that number is load-bearing. Those get a warning instead.

Windows-saved scripts 📄

The importer was quietly sensitive to line endings. Several patterns anchor to the end of a line, and a carriage return is neither a space nor a tab — so on a script saved on Windows they simply stopped matching, and the script imported as though those sections weren't there.

Line endings are now normalised before anything is parsed. Fixing that also uncovered a crash on a perfectly ordinary script, which is fixed too.


Download & run

  1. Download EggmansLaserForge-0.1.12-win-x64.zip below.
  2. Right-click it → Extract All… into a folder of your choice (e.g. C:\LaserForge).
  3. Open the folder and run LaserForge.exe.

If Windows SmartScreen shows "Windows protected your PC", click More info → Run anyway — unsigned indie software, and only the first time.

One-time setup for video conversion (optional)

Download the full FFmpeg build — ffmpeg-git-full.7z — from https://www.gyan.dev/ffmpeg/builds/ (under "latest git master branch build"; the full build, not essentials). Extract it anywhere, and when LaserForge asks, point it at ffmpeg.exe inside the extracted bin\ folder. It's remembered after that.

Requirements

  • Windows 10 / 11, 64-bit
  • Hypseus Singe — the emulator that runs your game (separate free download; grab the latest Windows 64-bit release — v3.x SDL3 and v2.12.1 both work fine)
  • Your game's .m2v video — or a source video (mkv / mp4 / webm) to convert

Fully self-contained and portable: the .NET runtime, FFmpeg playback natives, both global Singe frameworks, and the default game support files are all inside the ZIP.

See the README for a complete, beginner-friendly walkthrough.


SHA-256 — EggmansLaserForge-0.1.12-win-x64.zip
b9b2edac6fd39d6a2ed3bdaed074742c95825ba9123088ea59567395d491f162