You are browsing as a guest. Sign up (or log in) to start making projects!

RaidenTechnology

@RaidenTechnology

Joined July 18th, 2026

  • 10Devlogs
  • 3Projects
  • 3Ships
  • 48Votes
Open comments for this post

20h 58m 13s logged

RaidenScript: the cover is a program, not a picture

  1. I gave the project a red and black cover, and I did it without opening an image editor. The cover generator is a RaidenScript program that prints a plain PPM to stdout, so I changed the palette in the script and ran it again: three radial glows, a faint grid, a vertical gradient on the bolt, and a 5x7 bitmap font that is data inside the file, not a system font.
  1. It computes 315,000 pixels in 43 seconds. That is my tree-walking interpreter doing arithmetic in a while loop, with no graphics library in the program and none in the language.
  1. My first render was wrong and I had to say so. The old cover put a turquoise bolt on a violet field, so the contrast came from hue. In red on red there is no hue left to lean on, and the bolt sank into the background. I cut the background glow to a third and pushed the bolt to a near-white core, so the contrast now comes from brightness.
  1. I cleaned the repository behind it. My README still linked to SPEC.md and WORKLOG.md after I deleted them, so anyone cloning it hit two dead links on the front page. I removed those links and rewrote the root README and two demo READMEs without tables.

Why did I do it like this?

  1. Because a cover drawn in an image editor says nothing about my language, and a cover computed by it is the smallest honest benchmark I own. If the language cannot fill 315,000 pixels, I want to know that before someone else finds out.
  1. Because 43 seconds is a real number and I am not hiding it. It is fine for a cover I render once, and it is exactly why a bytecode VM sits on my roadmap instead of being marked done.
  1. Because a README that points at files I deleted is a promise I broke to whoever clones the repo, and that is the first thing a reviewer sees.
0
0
4
Ship

RaidenScript v0.2 Update

I wrote RaidenScript (RS) from scratch in C++20 as a small embeddable scripting language. In Ship #1 I had it running in three hosts. Since then I gave it a browser you can try it in, and I turned the Minecraft host into a real game running on a live server.

  1. I put the language in a browser. I compiled the C API to WebAssembly, served it from GitHub Pages, and wrote an editor with a Run button and four example programs: raidentechnology.github.io/raidenscript. I send nothing to a server; the page is the interpreter.
  1. I stopped treating the Minecraft host as a demo. I wrote 144 commits on it since the last devlog, and I keep the rules out of Java entirely: they sit in one .rai file the server loads at boot and reloads while players are online.
  1. I wrote a whole farming layer as script rules. Every crop got its own tool with its own level and rarity ladder, and I added tool enchants, XP bottles, an upgrade bench, and a 20 minute contest with medals and a shop of its own.
  1. I added pests. Twelve species, a spawn engine I tied to whatever crop is planted, a five-tier vacuum to kill them with, and a loot table I hooked into the same farming-luck stat every other system reads.
  1. I built a garden around it: 8 plots, a regeneration engine that rebuilds 13 farm patterns, 76 visitor NPCs I gave 53 signed skins, and a copper economy I priced off the live Bazaar API instead of numbers I made up.
  1. I added one host primitive, taslakModel, and let the script choose the model an item wears. Adding an item is now a script edit and nothing else.
  1. I fixed three bugs the live server found and no test of mine would have. An item-identity gate was cancelling every right click unconditionally, so the grappling hook silently never fired. The area hoe was catching its own synthetic event and moving the stack twice. And one wrong price in a shop table was failing the entire script at boot.

Why did I do it like this?

  1. Because I think a language is judged by what runs on it, not by its grammar. The strongest thing I can say about my embedding API is that a 10,000 line ruleset runs on it every night, on a real server, with real players.
  1. Because I lose reviewers at the install wall. For Ship #1 I asked people to clone a repo and build it with MinGW. Now I ask for one click, and I am handing them the same interpreter, not a simplified twin.
  1. Because every rule I move out of Java is a rule I can change without a restart. I left no compile step in the script layer, so /rai reload changes the balance of a live game between two swings of a sword.
  1. Because item models were the last thing still forcing me into Java. I closed that hole, and I now keep the Java side as infrastructure only: it knows how to talk to the server and nothing about what the game is.
  1. Because I wanted the contest, the pests and the visitor queue to stress the C boundary. I pass numbers in the array and let strings travel beside it, and not one of these systems made me widen that contract.
  1. Because a bug a player hits at 2 AM is worth more to me than a bug I find in my own suite. All three above came from the server.

Source (MIT): github.com/RaidenTechnology/raidenscript

  • 1 devlog
  • 31h
  • 10.41x multiplier
  • 104 Stardust
Try project → See source code →
Open comments for this post

30h 36m 36s logged

The loop that fixes the language is written in the language

Since the last devlog the work split in two: a development loop that RaidenScript now drives itself, and a repository that finally stopped mixing the language with a game server.

selfdev - the decision logic is a .rai file

tools/selfdev.rai holds the loop’s rules: pick an area nobody touched last round, run the build, read the log, call the pass green or red, append its own entry to a round journal. Under it sits tools/rsdev.cpp, a sys.* host built on the same C API the browser game and the Minecraft plugin already use. The language had three hosts. The fourth one is its own toolchain.

Pass 2 taught it what not to trust. tools/selfdev-build.cmd reported EXIT=0 for a run that failed to link. The cause is not in the language at all: exit /b inside a ( … ) » log 2>&1 block does not end a .cmd script, so control falls through to the final exit /b 0. A loop whose whole contract is “exit 0 means green” can commit on red - and it did once, on stale .o files. The rule now is read the log, not the exit code, and it is written in capitals at the top of SELFDEV-NOTES.md because I expect to forget it.

What the passes actually fixed

  • String building was quadratic. At 60,000 pieces: s += x took 1879 ms, s = s + x took 2382 ms, against a list.push + join baseline of 54 ms - 35x and 44x. Strings stay immutable (SPEC section 2), the fix is ownership: when the slot is the only owner, append in place. Both forms now run in about 25 ms. The indexed form d[k] += x was a separate code path and stayed quadratic after the first fix, so it took a second pass to catch.
  • The measurement size was part of the test. At 24,000 pieces the s = s + x check passed green: 288 KB fits in L2 and quadratic copying looks cheap in cache. At 60,000 (720 KB) both went red.
  • f-strings could not hold a quoted string inside an interpolation, although SPEC 9/3 said they could. f”{sys.read(“a.txt”)}” was a syntax error. The spec was right and the parser was wrong.
  • A blank line in a CRLF file closed a block early. The lone \r counted as content - the kind of bug that only shows up on someone else’s machine.
  • Two C API bugs: rs_call could return an empty error message, and a nested rs_call corrupted the string channel of the outer primitive.
  • The JVM bridge silently collapsed an int-returning host primitive to 0. Silent zeros are the worst class: the plugin keeps running and the numbers are simply wrong.

68 C API checks green, zero compiler warnings, sanitizer run clean. 29 keywords, still capped at 30. No C API signature changed.

The branch split

227 commits had piled up unpushed and 216 of them were the Minecraft plugin demo. I reset main to origin, cherry-picked only the 13 language and tooling commits, and left the rest on its own branch. main is the language. Nothing else goes there.

Most of the time logged on this project since the last devlog went into that plugin demo - the JVM binding exercised against a real server - and that branch is not on GitHub yet. The language fixes above came out of the unattended loop.I read every pass and hand-picked what entered main. The design, the spec and the original src/ are mine. Logged hours are my editor time only.

0
0
11
Ship

RaidenScript v0.1 — an embeddable scripting language written from scratch in C++20.

WHAT IT IS
A small language (29 keywords) with Python’s readability and JavaScript’s runtime model, built to go inside another program and make it scriptable. The same interpreter runs in three hosts, and each one has a working demo in the repo:

  • Terminal — native binary, “rai program.rai”, plus a REPL
  • Browser — C API compiled to WebAssembly, running an animated store page
  • JVM — C API through JNI, running a Paper Minecraft plugin

The demo link is my shipped game STAR BREAKER: one of its weapons is not defined in the game at all, it is a RaidenScript file the game loads and runs through the WebAssembly build.

WHAT IS IN IT
Lexer, parser, resolver, tree-walking interpreter and REPL, all hand-written; no parser generator, no dependencies. 16 example programs that double as the regression suite, 20 C API checks, 7 WebAssembly checks, zero compiler warnings. A recursion-depth guard so a runaway script raises a catchable error instead of killing the host process — verified separately on native, wasm and the JVM.

The cover image was computed pixel by pixel by a RaidenScript program (demo/kapak/kapak-ascii.rai) — no graphics library, in the program or in the language.

HOW TO TRY IT
git clone, then “make” (w64devkit/MinGW on Windows, gcc elsewhere), then “rai examples/01-temeller.rai”, or just “rai” for the REPL. The README is a full manual: language guide, standard library, embedding walkthrough. Known limits are written down there rather than hidden: an exception escaping a host function leaks stack, string += is quadratic, and a script returning a string to rs_call still yields 0.

AI SPLIT
The design of the language and its core — lexer, parser, resolver, interpreter, REPL, C API, string channel — is mine and was written by me. The host bindings, the demo apps, and this week’s seven interpreter bug fixes are AI-written from my designs and my audit. Logged hours are my editor time only.

Source (MIT): github.com/RaidenTechnology/raidenscript

  • 4 devlogs
  • 7h
  • 17.73x multiplier
  • 127 Stardust
Try project → See source code →
Open comments for this post

33m 28s logged

Seven bugs, and the ones that were never reachable

Last devlog promised a depth counter for the crash that hit all three hosts. I
built it, then kept reading instead of building, and the audit found six more.

THE COUNTER

A script that recurses too deep does not raise an error. It kills the process.
On a Minecraft server that means every player drops and the log has not one line
about it - no Java exception, no hs_err file, exit code 127.

The tempting fix is a bigger stack, but that only moves the wall: with -Xss16m
the JVM dies at ~5000 frames instead of ~500, the same way. So the interpreter
counts instead. Every script call goes through one function, and past the limit
it raises an ordinary Error - catchable with try/catch, readable through
rs_last_error.

Picking the number mattered more than writing it. My notes said “4000”, my
measurements said that was useless: native and wasm hit the wall near 1000
frames, a default JVM thread at ~500, because each script frame costs about
1.7 KB of native stack. A limit above the wall protects nobody. So: 800, and the
JNI bridge drops it to 400 because it knows what stack it stands on. The counter
also has to be restored no matter HOW a call exits - return, return signal, or
exception. My restore lines covered two of those three paths; a destructor
covers all three and cannot be forgotten.

A CRASH ANY SCRIPT COULD TRIGGER

"a,b".split(5)

That was enough to kill the host. split assumed its argument was a string,
dereferenced a checked cast without checking it, and got a null pointer. Eight
builtins had the same line copied into them. In a standalone language that is a
segfault. In an embedded one, any script author can take down the server with a
typo. It is now a type error naming the method.

OPERATORS THAT WERE NEVER REACHABLE

The lexer produced tokens for & | ^ « ». The interpreter had the arithmetic
for all five. Every test passed. But no rule in the parser ever read those
tokens, so “flags & 4” got “expression expected, found ‘&’”, and the code in the
other two layers had never once run. Three layers agreeing on a feature is not
the same as three layers connected by it. The fix was four precedence levels,
ordered like Python and C - no new keywords, so the language stays at 29 of 30.

THE SMALL ONES

throw Error(“amount missing”) reached the host as “”: the CLI path pulled
out the message field, the embedding path did not. 2 ** 64 wrapped silently to
0. 1 « 64 was undefined behaviour - on x86 the CPU takes the shift count modulo
64, so it quietly evaluates to 1. And an exception thrown from a
single-expression lambda left the interpreter in the lambda’s scope, so the
catch block ran against the wrong variables.

WHERE IT STANDS

The C API suite is 20 checks now, five written for these bugs; the wasm suite 7;
11 examples still run; zero warnings. The guard is verified on native, wasm and
JNI separately, and in each one the process was still alive after the limit was
hit. Three problems stay open, in the README rather than hidden: an exception
escaping a host function leaks stack, string += is quadratic, and a script
returning a string to rs_call still yields 0.

ON THE AI SPLIT

This changes what I said last time, so I am saying it plainly. Until now AI had
not touched src/. This round it did: I asked it to audit the interpreter and fix
what it found, and the C++ in these seven fixes is AI-written from that audit.
The language itself - lexer, parser, resolver, interpreter, REPL, C API, string
channel - is still my design and was written by me. Host bindings and demo apps
were already AI-written from my designs, as I said last devlog. Logged hours are
my editor time only.

0
0
24
Open comments for this post

3h 23m 47s logged

One interpreter, three hosts

Last devlog my language could define a weapon in my shipped game. Since then it
left the browser too, and the way out taught me more than the way in.

STRINGS WITHOUT LYING ABOUT NUMBERS

The C boundary only carried doubles. That was a deliberate choice: a number is
copied, so nobody has to ask who owns it. Then I tried to write a bank UI and
stopped in the first ten minutes. An IBAN is text. An error message is text.

The tempting fix is to pass a handle: “this double is really an index into a
string table”. I did not do it. A contract like that is invisible, and the first
time somebody misreads it the program moves the wrong money without a warning.

So strings travel BESIDE the numbers instead of inside them. args[] did not
change by a single byte, rs_host_fn kept its signature, and my already-shipped
game’s bridge kept working untouched. Text is pulled by the script, not pushed
by the host:

script:  iban = ui.input("iban")
host  :  reads the field name, returns the value

THE 64 KILOBYTE FLOOR

Then I asked whether the language could drive an animated web page, and measured
instead of guessing. 490,000 host calls per second. 200 elements updated twice
each = 1.45 ms, nine percent of a frame. Fine.

What was not fine: recursion died at depth 130. Not with an error - with memory
corruption. After it, the whole WebAssembly module was dead; I could not even
open a fresh VM on it. Emscripten’s default stack is 64 KB, and a tree-walking
interpreter spends several C++ frames per script frame. Adding -sSTACK_SIZE=8MB
moved the safe depth past 1000 and turned overflow into a catchable error. Cost:
the .wasm grew by three bytes.

Then a worse one. If a host function throws, the exception unwinds through the
interpreter’s frames without restoring the stack pointer, and that space never
comes back. I measured the drift: 5,000 escaping exceptions took the safe depth
from 1000 to 937. At 50,000 the module died. Fix: never let an exception cross
the boundary - catch it in the bridge.

THE THIRD HOST

Then JNI, and the language went into a Minecraft server. Same C header, a
different bridge, and the design copied deliberately: numbers in the array,
strings in the channel beside it.

The plugin is a custom enchanting table. Sixteen enchants, seven rarity tiers,
slot limits per rarity, conflicts, an XP cost curve, and the block of text drawn
on the item - all of it in one .rai file. The Java side is two classes that do
not contain a single enchant name. Stats are written into the schema my existing
combat plugin already reads, so enchants change real damage without one line
changing in the plugins that were already there.

Because there is no compile step, /rai reload changes the rules while players
are online.

A live server found a bug I had not: NamespacedKey only accepts [a-z0-9/._-] and
my enchant ids are camelCase. The bridge’s catch turned what would have been a
crash into a log line, and the log line named the key.

THE SAME BUG THREE TIMES

Native: depth 1000, then a silent death, exit code 127. WebAssembly: 130, then
corruption. JVM: 500, then the whole server dies with no Java exception and no
crash log. One cause - I recurse on the native stack and never count the depth.
Three hosts made it obvious in a way one host never would have. That counter is
next.

ON THE AI SPLIT

The core is mine: lexer, parser, resolver, interpreter, REPL, C API, the string
channel. AI has not touched src/ and I checked the history before writing this.
The host bindings and the demos are AI-written from my designs - the browser
binding, the JNI bridge, and three demo apps. Logged hours are my editor time
only.

0
0
12
Open comments for this post

2h 24m 13s logged

My language now defines a weapon in my shipped game

Phase 1 gave RaidenScript a lexer, parser, resolver, interpreter and REPL. Today it
got a design change of its own, and a way out of the terminal.

TWO KEYWORDS, AND WHY THE FIRST VERSION WAS WEAK

I had one keyword for pulling code in: use. I wanted hardware code to look different
from application code, so I proposed include for boards and import for everything
else. That rule was weak and I want to be honest about why. If include serial and
import math do exactly the same thing, nothing stops anyone writing include math. A
guide the compiler cannot check is not a guide, it is a convention with a syntax bill.

So the difference moved onto something the compiler can enforce:

import   resolved at runtime: std library, a git repo, a pinned version
include  resolved when the host builds you in

include now refuses quoted paths and @ “v0.3.1” version tags outright. On an ESP32
there is no filesystem and nothing to fetch, so a dependency needing the network at
load time simply cannot be an include. Hardware and software separate as a side
effect of a rule about resolution time — that is what earns two keywords instead of
one. A file may use both, and that is the point: that is what a bridge looks like.

29 keywords now, hard-capped at 30 until v1.0. One slot left, and I would rather
leave it empty than spend it on a synonym.

A BUG ONLY TURKISH COULD PRODUCE

Renaming use to import across 20 files, a bulk find-and-replace uppercased an i into
U+0130 — the Turkish dotted capital, because in Turkish that is what the uppercase of
i is. My editor did the case change in my own locale and handed C++ a character it
cannot compile.

The irony is sharp. RaidenScript deliberately accepts UTF-8 identifiers, so a Turkish
variable name is legal in the language I am building. The language I am building it
in has no such patience.

OUT OF THE TERMINAL

Then the embedding work: a C API, a WASM build, and one weapon.

The C API was not the hard part. Lifetime was. Function values hold RAW pointers into
the AST, and a host calls a script function long after loading it — so the VM has to
own the source, the tree and the interpreter, and destroy them in the right order. One
VM, one script; numbers across the boundary, not JSON.

Then WASM aborted on the first return statement. Emscripten disables C++ exceptions by
default, and this interpreter carries return, break and thrown errors on C++
exceptions. -fwasm-exceptions fixes it. The JS-based alternative would mean a trip to
JavaScript on every single function return — unusable for a tree-walker.

And the payoff: STAR BREAKER, the game I shipped last week, now has a weapon whose
definition is not in the game. scripts/plazma.rai holds its damage, fire rate, spread
and the shape of its shot. The engine calls fire(angle), the script calls back with
game.spawnBullet, and the engine makes the bullets. The script only says where.

Rarity, levels, reforges, evolution — all of it still works, untouched. The weapon is
an ordinary table entry that happens to be filled from a language I wrote.

I added a weapon rather than moving one: the game is shipped and its balance measured,
so adding risks nothing. If the WASM fails to load, the weapon simply is not there.

ON THE AI SPLIT

The design is mine — the two keywords, the bridge pattern, the decision to add rather
than move. I wrote the first pass of the rename; AI reviewed it, found what was
missing, and wrote most of the embedding layer with me directing. That is more AI than
my last devlog claimed, and I would rather correct it than let it stand.

Repo (MIT): github.com/RaidenTechnology/raidenscript

0
0
11
Open comments for this post

48m 5s logged

I wrote a programming language. It runs.

RaidenScript is an embeddable scripting language — the kind that goes inside
another program and makes it programmable. It borrows readability from Python,
the runtime model from JavaScript, optional types from C++, and declarative UI
from HTML.

Phase 1 is done: lexer, AST, parser, resolver, tree-walking interpreter, REPL.
~5,150 lines of C++20, zero warnings under -Wall -Wextra -Wpedantic -Wshadow
-Wconversion.

WHAT I DID FIRST WAS NOT WRITE CODE

Phase 0 was pure design: a spec, a grammar, and 15 example programs — and no
compiler at all. Writing those examples found 16 defects in the spec before a
single line of the implementation existed. Two of them would have forced a
rewrite later:

  1. Conditional expressions (a if cond else b) were missing from the grammar
    entirely — I typed one by reflex in example 4 and it couldn’t parse.
  2. => followed by { was genuinely ambiguous: block, or map literal? I took
    JavaScript’s answer — after =>, { is always a block, and returning a map
    needs ({…}). Finding this while writing the parser would have cost days.

Then the parser found a third: my rule that brace-blocks suppress indentation was
just wrong. It makes this impossible to write:

register("hit", (e) => {
    if e.critical:
        damage = damage * 2
})

Parentheses hold one expression; brace blocks hold statements, and statements
need block structure.

DECISIONS I’M HAPPY WITH

  • Only nil and false are falsy. 0 and “” are truthy, which kills the classic
    if-count bug at the root.

  • / always returns a float; // is integer division. C’s silent truncation is a
    bug factory.

  • No package registry, ever. Modules resolve straight from a git repo, Go-style.

  • I built the diagnostics engine before the lexer, because error messages bolted
    on afterwards always stay bolted on. Columns count UTF-8 characters, not bytes
    — so the caret lands correctly when a variable is named sayaç. Real output,
    Turkish for now (English messages are on the list):

    hata: beklenmeyen ‘!’
    –> test.rai:3:7
    |
    3 | c = 5 ! 3
    | ^
    |
    = ipucu: olumsuzlama için ‘not’ kullan

THE PART THAT MADE ME GRIN

Example 15 is a calculator — a lexer, a Pratt parser and an evaluator, written in
RaidenScript. I wrote it in Phase 0 as a rehearsal for the C++ I hadn’t written
yet. Now it runs on the interpreter it was a rehearsal for: 2 ^ 3 ^ 2 gives 512,
right-associative, not 64.

The REPL I wrote by hand, and it immediately earned its keep by finding a bug
nothing else could: Source::fromFile stripped the UTF-8 BOM, but the constructor
didn’t. The REPL was the first code path to build a Source from an in-memory
string instead of a file, so piped input carried a BOM into the lexer, which
glued it onto the first identifier. A new entry point finds bugs no amount of
staring finds.

ON THE AI SPLIT

The design is mine — spec, grammar, naming, and every decision above. Phases 0-6
of the implementation were written with AI assistance (Claude); the REPL, step 7,
I wrote myself, and that is the mode from here on: I write the code, AI reviews
it.

WHAT’S NEXT

Not phase 2. I’m skipping types and the bytecode VM and going straight to
embedding — because a tree-walking interpreter is plenty fast for mod scripts,
which run on events, not in the render loop. The goal: compile to WASM, bind a
game.* API, and define one weapon in my published game STAR BREAKER using a
language I wrote.

A language nobody uses is a toy. A language shipping in a real product is not.

Repo (MIT): github.com/RaidenTechnology/raidenscript

0
0
5
Open comments for this post

37m 18s logged

LIFE OF SOFTWARE — a typing game where the answers expire

Made for the GMTK Game Jam 2026, theme “Count Down”, in 96 hours.
Play: raidentechnology.itch.io/lifeofsoftware
Source (MIT): github.com/RaidenTechnology/life-of-software

THE IDEA

Everyone’s first instinct with “Count Down” is a timer. So I gave the game two.

You get one clock and twenty-five programming languages. Type the patterns of the
language in front of you — import, async, fn, =>, mov — and every correct one
buys back seconds, score and credits.

But while you type, the language itself is going obsolete. Every few seconds a
pattern you have not written yet is put on notice — deprecated since 3.11,
removed in 4.0 — and moments later it is gone for the rest of the level. Write it
before the notice dies and it pays double.

That is the whole game: racing one countdown while a second one eats the answers.
The theme isn’t decoration on top of a typing game; it’s the thing that makes the
typing hard.

THE PART I’M PROUDEST OF

Most typing games hand you words. This one hands you impl, nonlocal, »= and %>%.
If it stopped there you could clear HASKELL at speed and learn nothing about
Haskell — which felt like a waste of the player’s time.

So the game asks two questions before the run: how much software do you know, and
do you want the patterns explained? The first answer moves the ropes (opening
clock, assist, hint price) and never the score, so one personal best stays one
number. Say yes to the second and every time a language falls you get a CODE
REVIEW card: the patterns you typed, in the order you typed them, each with a
one-line meaning and a real example.

All 1276 patterns in the game carry one; 937 open a second, deeper panel. I
recounted these out of the shipping build before writing this — an earlier number
I’d been quoting was wrong, and I’d rather correct it than repeat it.

THE LADDER

25 languages, easiest to hardest: HTML, CSS, Python, JavaScript, … Rust, Haskell,
Assembly. Each has its own rules — JAVA and C# pay more for long patterns, BASH
and ASSEMBLY for short ones, RUST and HASKELL charge three seconds for a mistake.
Around it: bosses with an exploit chain (three patterns in order for triple
damage, break the order and the boss heals), a boon card every level, ten item
types across five rarities, festivals, and a daily seeded challenge.

TECHNICAL NOTES

Phaser 3, plain script tags, no bundler. There are no audio files in the build —
every sound is synthesised at runtime from WebAudio primitives, including the
per-language music profiles, the deprecation stings and the boss bed.

The trap that bit me repeatedly: itch.io serves updated builds from the same URL,
so every change has to bump ?v= in index.html. Forget it and the browser
cheerfully replays yesterday’s JavaScript. The shipping build is v41.

The four bugs I found on the last day all came from playing the itch draft rather
than my local copy. Local testing found none of them. Test where the players are.

ON THE HOURS

Honest note: this project shows almost no tracked time. My WakaTime plugin died on
22 July — the day the jam started — and stayed dead through all 96 hours. I only
found it today. The 204 commits in the repo are the real record of the work; I am
not going to claim hours I cannot prove.

AI DISCLOSURE

Design, direction and concept are mine. Parts of the code were written with AI
assistance (Claude) — the audio layer, the language data pass, the ending and
prologue scenes. No AI-generated art or audio assets are in the build; all sound
is procedurally synthesised code, which the jam rules permit. The same disclosure
is on the itch.io page.

0
0
3
Open comments for this post

1h 2m 34s logged

The void finally has a voice. 🎵

Until today the three void weapons — the game’s endgame centerpiece — all fired in complete silence. Hand-wrote a distinct sound for each in our tiny WebAudio synth (still zero audio files, every sound is code):

  • VOID SPEAR — a “void tear”: a short sawtooth rip, a hollow square-wave body, and a long sub-bass hum underneath. We auditioned three candidates (abyss whoosh / tritone ritual blade / void tear) and the tear won.
  • LIGHT WANDERER — an “electric blessing”: a rising triangle-wave triad (C5-E5-G5) with two square-wave spark crackles on top.
  • ASTRONOMIC WANDERER — split in two so it stays in sync with the meteor’s variable fall time: a falling whistle when the rock is called, and a deep quake + crunch at the exact frame of impact.

Each sound has its own spam gate (120-200ms) so ability-haste builds don’t turn the mix into porridge.

Best part: while testing I found the game’s audio unlock only lived on the Menu buttons. Any path that skips the menu ran the ENTIRE game silent — music included. One first-pointerdown listener in main.js fixed a bug nobody had ever reported.

The v=98 build is packaged and ships to itch tonight: https://raidentechnology.itch.io/starbreaker

Tomorrow: GMTK Game Jam. See you on the other side. ⭐

0
0
10
Open comments for this post

31m 17s logged

The biggest update since launch. There is a whole second half of the game now, and the first half plays very differently.

v1.1 shipped days ago. Since then I have been building and, more usefully, measuring — the game had a lot of numbers nobody had ever checked, including me.

THE VOID
From NORMAL up, a kill can tear the sky open. The void washes across the map, then purple elites arrive through separate rifts — WARRIOR (snares you), ASSASSIN (fast, triple damage), TANK (4x health, aura). Each gets one 50% revive. They pay VOID credits, a currency you cannot grind normally.

THE VOID BAZAAR [V]
Three armaments, an exclusive pick — taking one empties your gun slots. VOID SPEAR (cone slash; kills stack permanent armour), LIGHT WANDERER (light waves + chain lightning), ASTRONOMIC WANDERER (meteor strikes; revives you once). They are abilities, not guns: self-firing, scaling with ability haste, each with a max-HP bonus. Switching is free after paying the price gap once. Each evolves, and each has a red PREMIUM CAPSTONE card that rewrites it: time stop, heal-per-sweep, orbital judgement.

THE PILOT AND THE ARMOURY [G]
The ship is gone — you are a pilot who visibly wears what you equip. Four armour slots, seven rarities, eight reforges, its own skill ladder; UNIQUE pieces can be void-forged to double defense.

DOCTRINES
At floor 5 you pick one of three, once per run: GUNSLINGER, VOID-TOUCHED or ENGINEER. Each opens its half of the skill tree and closes the other two — runs finally diverge.

MATERIALS AND THE FABRICATOR [X]
Five materials from five activities, seven recipes, from repair kits to a permanent hull plate. The best reforges are now bought with VOID credits at a fixed counter, not gambled for.

SKILL TREE: 46 nodes, 16 tiers, 109 ranks. The far end costs millions on purpose.

PERSISTENCE: runs live in localStorage now. Plus TODAY’S EXPEDITION — one seeded daily run, same for everyone, one life — and the CORE FORGE, an endless meta tier.

BALANCE — WHERE I STOPPED GUESSING
I spent a day measuring. Highlights:

  • The anti-idle system had never once fired (menus reset its timer). Fixed — a parked ship dies in 30 seconds now.
  • Enemy health was clamped to your own DPS, and the clamp always bound: time-to-kill was exactly 2.00s at every floor of every difficulty, so every damage upgrade was cosmetic. The anchor is now a floor expectation; real damage only supplies guard rails (6.0s neglected / 2.0s kept pace / 0.5s min-maxed).
  • A void loadout emptied your gun slots, so the game read you as zero damage and INFERNAL died in one hit. Fixed.
  • Boss sizing only saw your active gun — a fifth of a real build — so bosses died in under a second. NIGHTMARE bosses now run ~8s.
  • Void damage compounded to x50 by floor 29. Capped at 2-3x — still the best chase.
  • The meteor is aimed by hand now: click to call it.
  • TRIPLE TRIGGER 25%→60% cost, SMG −35%, HOMING −75%→−30%, flat 50% death penalty, enemy count scales past floor 10, armour benefits from LUCK now.

Plus a long fix list: cone weapons missing targets standing inside them, time stop ignoring mid-stop spawns, pets flying into enemies’ faces — and two whole systems (ARMOURY, FABRICATOR) that shipped without appearing in any menu. The hotkey bar is colour-coded chips now, and there is a REFORGE CODEX.

KNOWN: a life’s first boss is still short — no data to size it yet.

WHAT’S NEXT: GMTK Game Jam starts July 22nd, so I am away a few days building something small. Feedback very welcome — especially if something feels too slow or a doctrine is the wrong pick. I have done enough guessing.

Thanks for playing ⭐

0
0
2
Ship

STAR BREAKER is a space-mining roguelite — break asteroids, hunt pirates, and climb from METEOROID to STAR through seven ascending difficulties, ending at THE STARBREAKER itself. It runs free in the browser, on desktop AND mobile: https://raidentechnology.itch.io/starbreaker

What I made: a full roguelite loop — 4 guns x 7 rarities with evolutions, reforges and synthesis, a 46-node skill tree, robot pets, orbital and deployable turrets, doctrines, a void event economy with its own currency, wearable armour on a layered pilot sprite, materials + crafting, a daily seeded expedition, and shielded, phase-gated bosses.

What was challenging: balance. I spent a full day measuring the game instead of guessing and found that enemy health was silently clamped to my own DPS — time-to-kill was exactly 2.00 seconds at every floor of every difficulty, so every damage upgrade I sold the player was cosmetic. Rebuilding the difficulty anchor (and fixing everything that fell out of it) is the work I am proudest of; the full story is in my devlog.

How to test: open the itch link in any browser — no install needed. Move with WASD, aim with the mouse (weapons auto-fire), B opens the shop, T the skill tree. On mobile you get a virtual joystick and auto-aim. Two minutes is enough to feel the loop; pick VERY EASY for a relaxed run, or HARD+ if you want the real scaling.

Built solo as my training project for GMTK Game Jam 2026. Open source (MIT): https://github.com/RaidenTechnology/star-breaker

  • 1 devlog
  • 21h
  • 18.71x multiplier
  • 187 Stardust
Try project → See source code →
Open comments for this post

21h 11m 59s logged

STAR BREAKER is live — free, in your browser, on desktop AND mobile: https://raidentechnology.itch.io/starbreaker

This game started as my training project for GMTK Game Jam 2026 (2 days to go!). I wanted to learn the full loop: build a game, balance it, polish it, ship it. Somewhere along the way it grew into a full space-mining roguelite:

  • Break asteroids, hunt pirates, climb from METEOROID to STAR
  • 4 weapons x 7 rarities — with evolutions, reforges and synthesis
  • A 37-node skill tree split into 3 stages — clear one circuit to reveal the next
  • Robot pets, orbital turrets, deployable ground turrets
  • 7 ascending difficulties with shielded, phase-gated bosses, ending at THE STARBREAKER itself
  • Full touch controls: virtual joystick, auto-aim, tap-to-target orbital strikes

At launch, everything you saw and heard was generated procedurally in code — no art or audio assets. Partly as a challenge, partly as prep for the jam’s no-AI-assets rule. (The v1.2 update has since added two AI-generated enemy sprites — I’d rather say that openly than let the old claim stand; details in the next devlog.)

The game launched July 17 and the code is open source (MIT) on GitHub: https://github.com/RaidenTechnology/star-breaker

What’s next: GMTK Game Jam 2026 starts July 22nd. After that I’ll come back to STAR BREAKER with balance patches — early feedback is very welcome.

Cheetah + railgun feels strong. Prove me wrong.

Thanks for playing ⭐

0
0
5

Followers

Loading…