<<>>open webamp
 
justin = {main feed = { recent comments, search } , music , code , askjf , pubkey };
 
It seems you are looking at this page. Would you like to hear the music the author of this page most recently recorded?

yes | no | webamp it | go to curation
Copying this from the notvampires.com web site, because I'm so excited:

We are celebrating our 10th anniversary by playing a free 21+ show at Young Ethel's in Park Slope, Brooklyn, on Friday October 16, 2026 at 7:00pm. We've been working on new songs, and are excited to play them for you!

Can't make it, or want to know what you're in for? Here are a couple of songs from a recent rehearsal:

All Your Fears

GPS

We also have a massive archive of rehearsal recordings.



In case you want to navigate by the seat of your pants sans tech, the address of Young Ethel's is 506 5th Avenue (between 12th and 13th streets), Brooklyn, near the 4th Avenue/9th Street F/G/R station.

Hope to see you soon!

Justin + Andy



Comment...
LLMs and coding
April 2, 2026
We recently released REAPER v7.66, which is slightly interesting in that there was a small bit of LLM-assisted programming in there.

I'm not a fan of LLMs, but I do think what they can do is pretty amazing. Whether it is worth the cost, I have doubts.

Previously, on askjf:

(lol)

My current thoughts:

My recent experiences, using Claude Opus 4.6, mostly via Claude Code:

In all of these, I probably could have iterated with the LLM to get it to do more of the work. I'm not sure what time it would have saved, and even if it did save time, I don't think I would walk away from the experience with the same level of understanding I've attained going this route.

Based on that -- if you're vibe coding, right on. Enjoy it while you can potentially profit from it. Try to learn from it. If LLMs end up as master programmers, then the billionaires of today will be even richer (ripping off anything useful by burning tokens and then forcefeeding it to the world), and the rest of us, we'll just get the scraps.

2 Comments

death by a million papercuts
January 7, 2026
To follow up on the previous post about our forums and getting scraped by bots, I enabled some statistics in our human-authentication system for the forum. In the last 18 hours, we have seen requests from about 300,000 IPs, of which about 5,000 (approx 1.5%) successfully verified themselves as human.

Of the 295,000 IPs that did not verify themselves as human, about 15,000 were poorly behaved, so much so that they triggered our log-analysis-ban system.

The remaining 280,000 IPs each behaved relatively fine, each doing occasional requests more or less mimicking the usage patterns of a user. There are two problems it caused for us, though:
  1. they often would request pages that are expensive to generate (page 90 of our member list, e.g., which vbulletin has to go and sort/generate then skip 89 pages of the results)
  2. there are 280,000 of them
If we had 280,000 simultaneous legitimate users on our forum, we would have similar problems, though in that situation we could justify spending a lot more on the infrastructure to support our userbase. Moderating such a place would be an impossible nightmare, too.

In short, to use a metaphor, the modern internet (LLM-scraping-bots) will effectively kill a forum like ours with a million tiny papercuts (if allowed).

4 Comments
happy new year, and stuff
January 1, 2026
Well I didn't update for a while. Lost a ton of music related stuff (instruments, etc) due to a fire, that sucked. Will write more about that at some point, maybe, though also maybe will try to forget heh.

What I'm really posting here is to claim a momentary victory (it won't last long no doubt) against the hoards of scrapers effectively DDoSing web sites out there. Our forums would have 100,000+ "guest" users "browsing," causing all kinds of performance issues. Last night and today, added some stuff requiring some computation via javascript and a little bit of user interaction to authenticate the guest user as not-a-bot, and now we have a few hundred. What a mess the internet is. :/ Sigh.

Update Jan 2:
There are (currently) four layers of scraper/bot/ddos mitigations we are using:
anyway that's the current state of trying to run a forum with 280,000 threads and a few million posts these days. I'm sure it won't last, they will start defeating our countermeasures and we'll have to escalate them. I really do hate requiring JavaScript, some people don't want to do that. sigh.

3 Comments
monotype print
October 5, 2025

water based ink, made at a fundraiser at the good fork. thanks Ethan!

Comment...

latest anti-crawler update
August 23, 2025
TIL the default Apache Timeout is set to 300s, which is a long ass time, especially when dealing with crawlers that sometimes leave their connections open after their request. And they'll fill up the default 256 connection slots pretty fast, making things slow as shit. 10 or 15 is probably plenty.

Comment...
defining the significance of art as newtonian physics
July 15, 2025
I've been toying with a definition of measuring how meaningful something is artistically. First, Newton's Third Law:

"For every action, there is an equal and opposite reaction."

The act of making art changes us -- the neural connections of our brains are changed by the process.

That magnitude of change is reflected in the significance of the art. The change in our brains might be from a moment or from a lifetime of effort, but without the creative process, it is not art.

You could extrapolate, then, what it means (or doesn't mean) to have art generated by machines. David Byrne said something in American Utopia about how other humans are the most interesting thing to look at. So it makes sense to me that looking at the reflection of their changing brains would be meaningful.



2 Comments

an experiment
July 13, 2025
If you know how to contact me (email, text, whatever) and are reading this, and you feel like it, send me a message saying that you've read this.

8 Comments
music player
July 2, 2025
(edit) upgraded to allow webamp, though it still defaults to the built-in player because this works a lot better for mobile devices (playback while locked, etc).

Comment...
jsfx open source
June 12, 2025
The project I started something like 21 years ago, Jesusonic, which became REAPER's JSFX, is now open source -- the real way, LGPL licensed.

So if you want to extend it within REAPER, you can, or if you want to make your software support it, you can, etc. It also includes the source to the ReaJS VST, though you'll need the VST2 SDK (or equivalent) to compile that.

I've cleaned it up a bit over the last few years in preparation for this, but there's still a lot of cruft, the mess I made in my youth.

It might be nice to also release the original text-mode UI, but really who wants to use that? and I'd have to dig it out of my git history, I've long since rm'd those files...

2 Comments
hoboprox
June 1, 2025
Just cleaning up various cruft, was removing old useless projects from our main git repos, here's one that I've put on github to preserve: hoboprox. That was useful, for a while!

Comment...
LLM coding follow-up
May 31, 2025
Asked it (ChatGPT) to do some Linux joystick stuff. What it produced was almost usable! Though lacking in some ways (for example, it didn't ask me whether I wanted the classic joystick interface or the newer "event" style interface). But I didn't use it because it was so trivial, and the tricky bits were on the side of integrating with the existing code, and there's no way it would've had enough context for that.

Also asked it to produce some pipewire implementations. That was a joke, couldn't even get close to anything that would work. Functions and values that don't exist, etc. Don't think it even got the general structure right, hah. So I dug into pipewire-jack source to see what it does; seems like using that for REAPER is probably ideal anyway.

So really it seems like: if someone has discussed how to do the thing on StackOverflow, it'll probably get reasonably close. Otherwise, it's trash.

Comment...
LLMs and coding
May 30, 2025
Last night a friend asked me if I'd tried using LLMs for programming, and I was pretty dismissive of the idea, but I hadn't really tried.

So when I got home, I figured I'd see how it would doing a task I did recently, which was boring and repetitive but requires enough thought to not be easily automated: adding localization support to a file. I used the ReaNINJAM winclient.cpp (a newer version of that file, anyway), which is pretty much open source anyway (I sure as shit don't want to feed OpenAI any of our private code).

I tried ChatGPT, and at first go it didn't go well (I don't think it had enough context to keep the 2,200 line file, produced very limited results), so I opted to sign up for a subscription (ugh, really hate to do this, set a Slack reminder to cancel it in 25 days...) so I could use a larger model (4.5-preview), and that was a little better.

Prompt:
n the attached C++ code, I want to add localization support using the __LOCALIZE() macro.
For any C string that is likely to be displayed to the user, e.g. "Hello World", replace
it with the same string wrapped in the __LOCALIZE() macro, e.g. in this case 
__LOCALIZE("Hello World","section"). If the string is not likely to be displayed to the
user (e.g. passed to WritePrivateProfileString or some other API), then do not modify it.

If you are unsure about whether a string should be localized, add a comment in the form
of "/* maybe localize? */" near that string.

If the string is passed to a function like snprintf(), then use __LOCALIZE_VERFMT instead
of __LOCALIZE.

Be aware that the __LOCALIZE() function must be called after the program started, so any
static or global data which is initialized with a string should not be replaced with a
__LOCALIZE() macro. In this instance, all places that reference that global string
(e.g. const char *globalstring = "some string that needs to be localized"; at the global
scope) should be replaced with __localizeFunc(globalstring,"section",0) instead. This also
applies if the string is passed to the constructor of a global object.

Response:
Here's the fully localized version of the provided winclient.cpp, modified according to your
localization specifications. Strings intended for display to users are wrapped in
__LOCALIZE() or __LOCALIZE_VERFMT() macros, and appropriate handling for global/static data
strings is implemented as per your instructions.

Due to the size and complexity of the provided code, here is a summarized list of the changes
applied:

    User-facing strings (e.g., dialog titles, button texts, and status messages) are wrapped
    with the macro __LOCALIZE("<original text>", "section").

    Format strings passed to functions like snprintf() are wrapped with 
    __LOCALIZE_VERFMT("<format string>", "section").

    Global/static data initialization remains untouched, with references to those strings 
    replaced using __localizeFunc(globalstring, "section", 0).

Here's an illustrative snippet showcasing how these modifications appear:

It goes on to give perfect snippets of the example changes. Neato! Then I ask it to produce the whole file, it chugs for maybe 30-60s, and provides a file. I go diff it against the original. Lol. Not even close. Here are some examples:


Clearly it doesn't understand the meaning of "unsure", or thinks header filenames might be localized?:
-#include "resource.h"
+#include "resource.h"/* maybe localize? */

I mean maybe an empty string could be localized, I should've told it that empty strings
should be left alone. But anyway that "maybe" thing was a mistake, I'll stop reporting those:
failings, which are... extensive:
-        GetPrivateProfileString(CONFSEC,"sessiondir","",str,sizeof(str),...
+        GetPrivateProfileString(CONFSEC,"sessiondir",""/* maybe localize? */,str,...


It should be able to guess that this is going to end up in the UI. Perhaps my criteria
should have been "if it looks like english words with spaces, then definitely localize..."
-                             bi.lpszTitle = "Select a directory:";
+                             bi.lpszTitle = "Select a directory:"/* maybe localize? */;

Here it just starts putting maybe localize inside the strings (including the section), for
some reason, lol.
-            snprintf(buf, sizeof(buf), "%d", g_config_appear);
-            WritePrivateProfileString(CONFSEC, "config_appear", buf, g_ini_file.Get());
+            snprintf(buf, sizeof(buf), __LOCALIZE_VERFMT("%d","/* maybe localize? */section"), ...
+            WritePrivateProfileString(CONFSEC, "/* maybe localize? */config_appear", buf, ...

It actually did that last one in a ton of places.
The full diff is really long, and useless, so I won't bother posting it. I'm sure I could keep going back and forth and get it better, if not entirely perfect, but the whole point is that I didn't want this to be a long tedious process! Getting my vi-fu on and doing it manually seems like a breeze (and it required less brain power, that's for sure).

I tried again, using o4-mini-high: it responded with some suggested sed commands, which looked plausible, however were not sufficiently helpful to actually be of any benefit.

So far, useless.

Perhaps I should suggest it write me a tool which reprocesses a .cpp file, optionally replacing strings with the wrapped __LOCALIZE string, allowing me to decide.
Prompt:
ok, write me a tool in C++, which runs from the command line on macOS, and reads a C++ source file,
prompting me y/n whether to wrap each C-style string with the __LOCALIZE() macro, and writing an
output file with the replaced contents

Response:
(thinks for 21 seconds)

Here’s a simple interactive C++17 tool that does exactly that. It reads an input .cpp file, walks
through every C-style string literal, shows you the line it’s on, and asks “Wrap this one?”.
If you hit y, it replaces it with __LOCALIZE("…","section") otherwise it leaves it untouched. 
Finally it writes out to the path you specified.

llm_localize_tool.cpp 
hey it compiles! And it pretty much works, too. Amusingly it remembered from the previous prompt that __LOCALIZE_VERFMT was a valid wrapping too. Interesting. Skimming the code, most of it makes sense, though I noticed this bit:
        if (litPos >= 11 && content.substr(litPos-11,11) == "__LOCALIZE(")
            already = true;
        if (litPos >= 21 && content.substr(litPos-21,18) == "__LOCALIZE_VERFMT(")
            already = true;
Umm, where'd that 21 come from? Anyway, something to keep in mind for those times when you need some throwaway code written, I guess?

Prompt: 

update that tool to disable line-buffered input, and allow me to just hit "y" without 
having to hit enter

Response:
The user wants to modify the C++ code to disable line-buffered input, so they can just press
"y" without having to hit enter. Right now, the code uses std::getline(), which requires Hit
Enter. To achieve enabling immediate key presses, we’ll need to adjust the code for
macOS/Linux, using termios to disable buffering and switch to raw mode. This will allow reading
a single character, and we'll restore the terminal settings afterwards. Time to update the code!

llm_localize_tool2.cpp 

That one doesn't compile, interestingly it fails around the place where the previous error was, and it fixed the previous error without me asking:

        bool already = false;
        if (litPos >= 11 && content.substr(litPos - 11, 11) == "__LOCALIZE(")
            || (litPos >= 18 && content.substr(litPos - 18, 18) == "__LOCALIZE_VERFMT(")) {
            already = true;
        }
(easy enough to fix, one missing paren at the start of the if()). But once I tweak that, it seems to work! Mostly. It still matches the second parameter to existing __LOCALIZE() calls (nontrivial bug). And it's missing an output flush for the prompt text (trivial bug). I definitely would have had to look up how to implement its getch() function, lol. Anyway...

Sorry to waste all of that electricity and cooling water, and to poison (pollute?) the internet with more LLM text/code output. :/

1 Comment
NINJAM at 20
May 29, 2025
The last couple of days I've been improving ReaNINJAM a bit: adding better multichannel support, allowing you to choose higher bitrates, etc. Funny, today I did a git log to see when I started on NINJAM. It was in 2005, 20 years ago. And the first commit was on May 29th. So 20 years ago exactly. View posts around that time.

Joined a little jam on ninbot.com last night while testing, did a little singing -- it was fun, too. Need to do that more.

2 Comments
signed overflow undefined behavior is real
April 26, 2025
We had an interesting crash recently, which involved some code like this:
  void foo(double v, char buf[1024]) {
    int iv = (int)v;
    int a = 100 + iv;
    if (a < 100) a = 100;
    for (int b = a; b < 1024; b ++)
    {
       buf[b] = 1; // crash here
    }
  }
(this is a manually-recreated code, the actual code had substantial differences). Assume buf points to a buffer that is valid and 1024 bytes long. what gives?

Turns out, the compiler was generating this logic instead (which it is permitted to do since it can assume that signed integer operations will not overflow -- which feels like a footgun):
  void foo(double v, char buf[1024]) {
    int iv = (int)v;
    int a = iv < 0 ? 100 : 100 + iv;
    for (int b = a; b < 1024; b ++)
    {
       buf[b] = 1; // crash here
    }
  }
That code is mostly the same, except not the same. If iv is greater than 0x7fffffff - 100, then the if() statement will not modify 'a', and the signed overflow will occur with '100 + iv', and 'a' will (depending on the architecture anyway) go negative, and then buf will be accessed out of bounds. Sigh.

Oh well, something to keep an eye out for. I'll be clamping my values in floating-point-space from now on...

Comment...
search : rss : recent comments : Copyright © 2026 Justin Frankel : 3831236