2025 #DecemberAdventure 1st

Added rotation optimisation to my grid layout to avoid clashes. Filling items left to right top to bottom: pick the rotation (by trying many rotations and picking the best) of each next item that maximizes the minimum (squared) distance between the item and the items immediately left and above.

made a page that might have less frequent updates maybe with slightly more detail:

https://mathr.co.uk/web/december-adventure-2025.html

December Adventure 2025 :: mathr

Low-key code-based fun in December 2025.

#DecemberAdventure 1st: afternoon

fixed up some almost-finished things in #clive (my thing for #LiveCoding audio in the #C programming language), namely separate in/out channel counts. Still hardcoded at build time in `server/config.h`.

at the moment:

jack: 2 in 18 out (currently set up for first 2 stereo, last 16 to exwhyscope for debugging)
portaudio: 0 in 2 out
sdl2: 0 in 2 out
stdio: 0 in 2 out

I put a prototype in the dsp.h header so that these changes will fail at build time instead of crashing. this also means client user code needs to change from

typedef struct { ... } S;
int go(S *s, int channels, const float *in, float *out, event
s_t *events) { ... }

to

struct S { ... };
int go(S *s, int inchannels, const float *in, int outchannels, float *out, event
s_t *events) { ... }

all branches in the clive-core repository are now up to date.

```
git clone https : / / code.mathr.co.uk / clive-core.git
```

(no web access any more due to bad robots, but the git tool should still work)

#DecemberAdventure 2nd: morning

did a fair amount of work on #clive, combining the separate client (compiler) and server (audio + osc) into one program, renaming lots of things to be more consistent.

the main advantage of this change is that the audio part can tell the compiler what the audio engine's sample rate is, so no need to define it by hand in the `live/clive.c` file (previously `client/go.c`.

I tagged as v1 the version before all these disruptive changes.

Summary of user-facing changes in the last couple of days:

```diff
diff --git a/client/go.c b/live/clive.c
index 84f7163..0b65cae 100644
--- a/client/go.c
+++ b/live/clive.c
@@ -1,13 +1,12 @@
typedef float sample;
-#define SR 48000
-#include "dsp.h"
+#include <clive.h>

-typedef struct
+struct clive_state_t
{
int reloaded;
// state
-} S;
+};

-int go(S *s, int channels, const float *in, float *out, events_t *events)
+int clive_audio(clive_state_t *s, int inchannels, const float *in, int outchannels, float *out, events_t *events)
{
if (s->reloaded)
@@ -25,5 +24,5 @@
// output
- for (int c = 0; c < channels; ++c)
+ for (int c = 0; c < outchannels; ++c)
```

and run ./bin/clive.sh instead of ./launch/local-native-sse.sh

#DecemberAdventure 2nd evening

tl:dr clive for windows wip

factored out the platform-specific code in clive:

- inotify used for file watching (linux, also freebsd15)
- dl used for loading compiled code (posix)
- sockets used for osc server (posix)

will probably switch from C11 threads.h (not supported by llvm-mingw) to pthreads for better portability

audio backends still need to be checked, possibly some non-portable bits lurking.

started a windows version of the file watcher but it doesn't seem to work when built with mingw64 and run with hangover-wine in termux on android, not sure if it's my code or the system.

the strategy i have for platform specific code is a layout like

watch.h
linux/watch.c
windows/watch.c

and the build system picks the correct implementation. better than a confusing mess of ifdefs imo.

it was the system (works fine compiled with mingw run with wine on x86_64 desktop debian)

#DecemberAdventure 3rd

clive for Windows now works (at least in Wine on Debian)!

still misses the OSC server but I never used it much anyway

I learned more than I wanted about BAT scripts.

build system is set up to cross-compile clive.exe from Debian, but clive.exe invokes onchange.bat which compiles clive.c to clive.dll using windows llvm-mingw.

had big issues with my trixie desktop not configuring the network when plugging in my bela single board computer (worked fine with bookworm laptop)

after plugging, ip addr listed:
....
9: enxbe1a04f85a10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
link/ether be:1a:04:f8:5a:10 brd ff:ff:ff:ff:ff:ff
10: enxbe1a04f85a13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether be:1a:04:f8:5a:13 brd ff:ff:ff:ff:ff:ff

I added this to //etc/network/interfaces.d/bela:

auto enxbe1a04f85a13
allow-hotplug enxbe1a04f85a13
iface enxbe1a04f85a13 inet dhcp

and service networking restart

now ip addr shows

10: enxbe1a04f85a13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether be:1a:04:f8:5a:13 brd ff:ff:ff:ff:ff:ff
inet 192.168.7.1/24 brd 192.168.7.255 scope global dynamic enxbe1a04f85a13
valid_lft 451sec preferred_lft 451sec

and bela.local IDE works in browser (presumably ssh too, need to test that next)

#Bela #networking #Debian

did not figure out how to get bela to access internet via host's usb so I just plugged in an ethernet cable....

compiling rtosc on bela is a bit tricky (old base system, rtosc expects cmake 3.10 but bela has cmake 3.7.2).

so far i patched the cmakelists to an earlier version and commented out the lines that broke it (mainly target_link_directories)

some errors still reported like

CMake Error at CMakeLists.txt:181 (add_executable):
CXX_STANDARD is set to invalid value '17'

but it built

now the problem cross-compiling the live C code from Debian Trixie is

DLERROR: /lib/arm-linux-gnueabihf/libm.so.6: version `GLIBC_2.29' not found (required by ../tmp/clive.a.so)

which I think means Trixie is too new

#DecemberAdventure 3rd evening + 4th morning

got #clive audio engine working on #Bela with clive (cross-)compiler running on desktop in a Debian Stretch chroot (matching the version of the Bela base system). very long setup instructions now in the README.md at

git clone https : / / code.mathr.co.uk / clive-core.git

compilation on desktop is much quicker than compilation on Bela (even with sshfs transfer overhead), allowing for lower #latency response to code changes.

no #OpenSoundControl #OSC server yet, because no #C11 <threads.h> support - switching to pthreads would probably be more portable...

clive is my thing for #LliveCoding #audiio #DSP in the #C programming language

idea: put the Bela context and audio block index n into the state struct, so that clive audio callback code can access Bela I/O other than stereo audio in + out.

this would be a backwards incompatible change, need to see how to minimize disruption... possibly it would be a good time to also expose the underlying block-based processing to the clive.c code, so that it can be inlined/loop-unrolled/etc for better runtime efficiency.

4th afternoon: documentation time, including updating my december adventure page. also some cross-compilation build-scripts updates.

DecemberAdventure 5th afternoon

spelunking in the depths of my messy code archives (a git repository with over 800 branches) to find sources for completed and released music pieces.

so far found 3 and got them running again with current clive-core:

- Pegasus v3.7
- Charred Bard
- Gradient

a few other searches were fruitless so far

- Binaray (from 2017 afaict, but the code I found is just a small part)
- El Day De Lay (similar issue, plus the released versions were probably live-coded takes)
- Crossbowed (nothing found)

more on my todo list
https://mathr.co.uk/web/clive.html#Tracks

In future I'll use separate files in one git branch instead of different branches for different tracks, much more maintainable. and periodically rebasing my project branch on clive-core/main seems more sensible than merging, in terms of knowing what really changed between the branches...

Clive :: mathr

Live-coding audio in the C programming language.

DecemberAdventure 6th

made a (-1) return value from clive_audio() callback mean "stop running".

thought a bit about how to send events from audio thread to the engine for backend control or things like OSC sending... needs more thinking before I start implementing.

thought about writing a sound file recorder, needs realtime-safe ringbuffer from audio thread to i/o thread. I do have a ringbuffer for sending from osc server to audio, need to check if I can reuse it.

want sent events to be synchronized with the audio stream, so I can do things like sample-accurate file-splitting with track metadata etc

found more of my old tracks' source code and copied them into my new multi-file branch. made a small player to step to the next once each is done. using a union to save memory, but this means the tracks can't blend with each other.

9 tracks so far, a little over 40mins. need to do a level-matching pass as some are much louder/quieter than others.

#DecemberAdventure 7th

found+fixed a #bug : part of one track was using single precision float instead of double in one critical segment; it worked ok at 48kHz sample rate but at 96kHz it reached #FloatingPoint precision limits leading to a counter failing to increment past a certain point, meaning the track end condition was never reached...

DecemberAdventure 8th - no code today

DecemberAdventure 9th - first pass at matching relative track volumes (each track separately to -23LUFS then collection to 0dBFS, working out somewhere around -14.8 LUFS). subsequent passes will do by-ear comparisons. process: using Audacity, note down peak level in amplitude dialog before+after loudness normalisation, copy numbers into code

DecemberAdventure 10th

wanted to add a new mode to some WIP camera processing algorithm, but couldn't find the non-size-coded version and spent all the time I had available reminding myself how it worked.

DecemberAdventure 11th

some small experiment with buddhabrot style rendering of hopfbrot, nothing impressive due to curse of dimensionality

DecemberAdventure 12th

no code today

DecemberAdventure 13th

I fixed the perturbation code in inflector-gadget - now uses 1 reference per inflection point and 1 extra for image center. hopefully correct rendering in more circumstances now. thanks to FractalLion on fractalforums.org for the push and a test case (which previously failed, now works).

also added variable wave support to the inflected renderer, which changes appearance of old parameters (so another url update coming for the web version so as not to break existing links; will need to minor update the old versions to have options to fwd to the new).

new release coming very soon, testing appreciated especially within the next 24 hours:

git clone https : / / code.mathr.co.uk / inflector-gadget.git

and check the README.md for build instructions

#InflectorGadget #DecemberAdventure

DecemberAdventure 14th

- released a new version of Inflector Gadget:
https://post.lurk.org/@mathr/115717273423449239

- started implementing an importer from Kalle's Fraktaler 2 KFR parameter files into Fraktaler 3 (F3 shares almost 0 code wtih KF2). so far coordinates from basic Mandelbrot set work, but no other formulas; also no colouring information. also images in fraktaler 3 are flipped vertically compared to KF2 (unless exactly one of KF2 and F3 has their respective "flip" option enabled).

claude (@[email protected])

Attached: 1 image Inflector Gadget v0.10 released, with bugfixes to the perturbation implementation (thanks FractalLion) and support for variable wavyness everywhere: source tarball and windows binaries available here: https://mathr.co.uk/web/inflector-gadget.html#v010 web version available here: https://mathr.co.uk/ig/4/ example: https://mathr.co.uk/ig/4/#cHJvZ3JhbSA9ICJpbmZsZWN0b3ItZ2FkZ2V0Igp2ZXJzaW9uID0gIjAuMTAiCmZvcm1hdCA9IDQKY2VudGVyID0gICAgICAgICAgICAgICAgICAgICAgICAgICAiKDcuODYxNjM1MjIwMTI2MDA2MDA5MDkxOTI4Mzc2MzM4Njc2MTkzMjA4ZS00IC03Ljg2MTYzNTIyMDEyNTUwNjk5NDA0MTMyODUxMDIzOTEzMTQ5NTcyMmUtNCkiCnJhZGl1cyA9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLjAKd2lkdGggPSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyNDc0CmhlaWdodCA9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEyNzIKcXVhbnRpemUgPSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlCmFsZ29yaXRobSA9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIKcmVmZXJlbmNlID0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKC0xLjE1NTc1MjA5NjQzNjA1ODUzNTMyMjQ2NTAzODE5MDQ2OTAzMTcwNyAxLjU4MTQ5ODk1MTc4MTk1NjY0Nzc2MzE5NDcxNzU3NDc5ODA4MTgwM2UtMikiCmluZmxlY3Rpb24gPSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEwCnBlcmlvZCA9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDIKY3Jvc3NoYWlyID0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZQp3YXZlID0gWy0xNDIuMjI5ODg4OTE2MDE1NjI1LDIwNDUuMjYzMTgzNTkzNzVdCmFjY2VudCA9IFswLjAxMDU4NiwwLjM3NjU2LDAuNzI3NzYsMC43NjA0NV0KaW5mbGVjdGlvbnMgPSBbCiAgICAiKC0xLjc2NjQxMDM0NTIwNTc0MzE2MjY5MjI4ODAzMzUzNzMwMzk5ODE5MyA0LjE3NDE0MDc3NzM5NTc1OTg3MDAxNzI0NzQ5NjE5MTc0MzI0ODA4MmUtMikiLAogICAgIigtMy4wOTU1MjAxNTM3MzU5NTI0MzczNjUyMzMzODczMzE3MDM1MDQ4OTIgNi4wMTgwOTU2MzM2ODU1MjA0MTQxNTIyMzc3MjI3MzQ0MDIyMzc2OTVlLTIpIiwKICAgICIoLTQuMjQ3NzUyMDc3MDg5MzEyMDgwNjgwMzk4MjU4MzAwNjMzNjMzMTU3IDYuODE1NDkwMTEwNDk2NzM4NDI3MjA0ODgyNzk3MDgyNTUyMzY1MzI1ZS0yKSIsCiAgICAiKC01LjMzODMyMjgyOTA5ODY4ODUxNDY3NzgxMjA4MjIyNzg1OTIyMTE3MiA4Ljg5MTc0Mzg1NzYxMTQ4Nzg1NDY3MzM2ODE3OTk3ODY0NjI2Nzg5MGUtMikiLAogICAgIigtNi4zODE4MTM1NTY0NTc4MjMzMDY1NzY3NzAwMDkzOTQ1NjUxMzYwMzEgOS44ODg4OTM5NjQyMDM1NDUyNDQ1ODM2NjYzNDM2MDY3ODk1NDc4MTllLTIpIiwKICAgICIoLTcuNDk5NDIwNzk5MjU4NjQzNjcyOTA5NjMyNzA5NzUzMTMwMDQ4MjA4IDEuNDEzOTE1NzY0ODM3NjQ3OTg4MzA2OTk0MzExODg0NjA2NTA4ODg5ZS0xKSIsCiAgICAiKC04LjU1NTc4MjY4MTczODUzMDM2NTU0OTA4MDAyNTY2NDM5MTg3NjIwNSAxLjYxNjU3MDkzNzk2MDM2MTU5OTc5NDg2ODg3NzAzNzEwNDg1MzQwNmUtMSkiLAogICAgIigtOS43ODY3MTk1OTk1Nzg0NTcwMDUyMDQwNzc4ODkwNTYxNTYwNDkwMzkgMi4wNjk3NzM2OTM3MzY0OTY2NDQ0NzM1MTkxMTY0NTQ5NjAzODUyNzZlLTEpIiwKICAgICIoLTEuMDg5Mjk2MzI5NTczODk5NzAzMzIyNDkwNjEwNDQzMTAxNDM0Mzc0ZSsxIDIuMjg3MDYyOTE0NzUxNjczNjU3NTU2MTE1NjUwMTE2NjAxNDIwMTg1ZS0xKSIsCiAgICAiKC0xLjIyMjkxNTkyOTUzNTQ5MTkxNDE1MjU4MjExNDk1ODQ1NjYyMjAyMWUrMSAyLjYxOTc4MDYyMjc1MjM1ODY5Njg4ODk5NTc2Mjc4ODE2NDcxODgxOWUtMSkiLApdCgo= #InflectorGadget #MandelbrotSet #JuliaSet #Fractals #MathArt

post.lurk.org

DecemberAdventure 15th

- released a new version of Fraktaler 3
https://post.lurk.org/@mathr/115724383593510517

- minor adjustments to my hopfbrot buddabrot code, nothing nice to look at yet...

claude (@[email protected])

#Fraktaler #Fraktaler3 v3.1 #release #DeepZoom #hybrid #EscapeTime #fractals #MathArt #MandelbrotSet #BurningShip binaries for Web, Android and Windows, built from Debian Linux, source for all those platforms https://fraktaler.mathr.co.uk/ changes since v3 (June 2025): - histogram colouring - partial KFR importer (no colouring yet) - web version uses full window canvas - doubleexp number type - float128 number type (where available) - fix: correct matching between appearance of CPU and OpenCL backends (thanks to JWM for reporting) - fix: compatibility with toml11 v4. - fix: builds and runs in Termux + Termux X11 on Android - fix: no 'quit when done' option on Web or Android. - fix: correct status when pressing ESC after rendering is complete. - internal: build system improvements.

post.lurk.org

DecemberAdventure 16 + 17

made a small thing to find attractive periodic orbits in the burning ship fractal. resulting 4d point cloud is projected via 3d to 2d using opengl+glsl with additive blending in code i had written some time ago, then tonemapped similarly to fractal flames. the 4th coordinate determines colour.

i'm not sure why but even though there are fewer points than my similar point cloud for mandelbrot set, rendering it is noticably slower. maybe point order matters a lot? or amount of overlap?

DecemberAdventure 18th

combined the point cloud generator and viewer into a single program, using Win32 API and compiled to i686 so Crinkler could do is magic. just under 4kB. later I may try to port the point cloud generation to glsl, which should be faster, and hopefully smaller, maybe enough bytes for some sound.

DecemberAdventure 19 am

Got tidal-core working with current MicroHs. (#) needs spaces like ( # ) because UnboxedTuples steals syntax.

Semi-blocked on the point cloud thing because wine on netbook does not find 32bit opengl, and hangover-wine in termux is currently unable to run x86 (or x86_64) binaries. i can run x86_64 on netbook, but crinkler is i686 only...

DecemberAdventure 19 pm

ported point cloud generation to opengl shaders. added some simple bytebeat sound. size above target, can probably reduce a bit by simplifying matrix operations - ideally remove custom sin/cos implementations. not sure if I'm following good practices for getting opengl function pointers. glsl could also be shrunk a few bytes i think.

DecemberAdventure 20

setting up new laptop. no coding, just getting some of my existing code compiled.

DecemberAdventure 21

got fraktaler-3 v3.1 compiled on new laptop, but gpu/opencl failed (even crashed desktop session once with a gpu reset). upgraded kernel and mesa to trixie-backports, now it complains about missing double precision support - if the igpu does not support it, so be it, but fraktaler-3 should be able to detect that case and simply not use double when unavailable. on v3.2 todo list (v4 will be kf2 palette compat, v5 merge with zoomasm).

DecemberAdventure 22

no code today. probably no code until maybe 28th. will see...