watercolor portrait of a friendly punk woman with long black hair and a red outfit autumn

december adventure

table of contents

โ„๏ธ

December Adventure is an online event where people pick a project to work on , and log their progress throughout the month of December . you can see a list of adventure logs written by other people here !

2025

december reflection

this month was difficult for reasons i've mentioned , and many i haven't . so i can't say that December Adventure was easy to keep up with , but i'm still glad i commited to it . it was a solid experiment to see if i enjoy logging my creative pursuits in a more routine manner . it turns out , i really enjoy it ! a few reasons why :

here's a recap of the most notable things i did this month :

thanks for following my adventure this month ! i'll be returning to write more logs here soon . i hope the new year brings comfort , healing , and agency for you and your loved ones ^-^

๐Ÿ’š๐Ÿฉต๐Ÿ’œ๐Ÿฉท๐Ÿงก

๐ŸŽง song of the day :
Studio Sorcery โ€” Kormac


december 30th

the self-hosting saga continues . it will continue for a while , because this is proving to be very time consuming , especially considering that i'm not a very fast programmer . that said , i did get the main structure pulled out :

* compile pyra program
  - first command line argument -> backend
  - second command line argument -> path
  - collect abstract syntax tree
  - import libraries and files
  - analyze strides
  - analyze rule effects
  - generate values for unbound variables
  - gather unique first stacks
  - generate code output

and also made a little library for the ordered table data structure , since it's used so often in the compiler :

* insert Value at Key in Table
  - Table keys: Key
  - Table at Key: Value

* get value at Key in Table
* Table at Key: Value
  - result: Value

| Rule for all keys in Table
* Table keys: Key
| Table at Key: Value
  - seen: Key
  - Rule - for: Key Value

* Rule for all keys in Table
  - reverse table keys

| reverse Table keys * seen: Key
  - Table keys: Key

* reverse Table keys

minor plan alteration : it looks like we'll need to double up the AST to continue incrementally rewriting the compiler . build the new AST within pyra , while keeping the Lua AST so that the unconverted functions still work . like a temporary support beam to keep the building upright while we replace the walls one by one !

the functions i've converted so far have ended up being a similar amount of code , so the pyra version of the compiler shouldn't be too much more verbose . here's an example Lua function :

function collectImports()
  local imports = {}
  if stacks_['include file'] then
    for _, fact in ipairs(stacks_['include file']) do
      table.insert(imports, fact.tokens[1])
    end
    stacks_['include file'] = nil
  end
  if stacks_['use library'] then
    for _, fact in ipairs(stacks_['use library']) do
      local path = format('./_/libraries/_.nv',
        pyraPath, fact.tokens[1])
      table.insert(imports, path)
    end
    stacks_['use library'] = nil
  end
  return imports
end

that i've rewritten to :

| collect imports
* stacks at [include file]: Fact
* Fact: Tokens PossibleMatches
* Tokens: Path
  - imports: Path

| collect imports
* stacks at [use library]: Fact
* Fact: Tokens PossibleMatches
* Tokens: Library
| compiler path: Compiler
  ~ join! ./ Compiler /libraries/ Library .nv ~
  ~ imports = @ result ~

* collect imports

pattern matching is so cozy ๐Ÿฅฐ . for anyone following along , i've been pushing progress to the self-host branch of the repo .

๐ŸŽง song of the day :
Animals โ€” Bonobo


december 29th

today was the first day pyra compiled itself !

before you get too excited , it's still 99% Lua . but the loop has been drawn ; our foot is in the door .

it didn't take long , but there were a few snags :

๐ŸŽง song of the day :
Timestopper Tactics โ€” corru.works


december 28th

i've really been enjoying CSS lately , so i felt like going on a side quest to update the style of my music website . the original design was from over four years ago ! here's a before and after comparison :

my music website before my music website after the redesign

along the way , i learned that you can set font-synthesis: none to prevent browsers from attempting to synthesize bold , italic , or other typefaces that are missing . this is useful because browsers synthesize them differently , which can make styles like -webkit-text-stroke painfully inconsistent .

the website was hosted on Netlify , and stored in a GitHub repository : two services that i'd like to move away from . Neocities has been looking more attractive as a primary host after learning that they have a CLI tool !

an issue i encountered when trying it out is that the tool doesn't support multiple sites , but you can work around this by using the NEOCITIES_API_KEY environment variable . in my case , i created a git ignored .env file with the following :

export NEOCITIES_API_KEY=<key from the dashboard>

and a makefile with a publish target like this :

publish:
	. ./.env; neocities push site

now i only need to run make publish to upload changes , and each website can have its own API key in a .env file .

the tool is also pretty slow for sites with a lot of files , because it makes an API call for every file during the push . i'm going to try out alternatives like async-neocities to see if they're any faster .

qualms aside , i'm happy to say that the site is now fully migrated to Neocities , with the repo on SourceHut . i'd like to do the same with pyra.run , and possibly this site ! i love the social and exploration features of Neocities , and it'd be great to have more of my work on the platform .

๐ŸŽง song of the day :
Wormhole โ€” KAMI


december 27th

time to make pyra self-hosted ! this is a somewhat large undertaking , considering that pyra.lua plus all six backends combine to around a thousand lines of Lua . why go through all of this trouble ?

here's the plan of attack :

  1. throw pyra.lua into one massive rule snippet , and compile it using the Lua backend
  2. recursively split up the snippet
  3. extend this process to each backend
  4. once snippets have fully receded into the realm of standard libraries , it'll be self-hosted enough for my purposes !
  5. at that point , it should be trivial to add Go snippets to the required library rules , and compile pyra to binaries for each platform

in other news , i used the docs generator on this website's SSG , and made a long overdue update to pyra's changelog .

๐ŸŽง song of the day :
Rock Lock โ€” Kognitif


december 26th

recap time ! since the last update , i finished up the documentation generator ! here's a rule that tells you what all of the different elements are :

visual representation of a self-documenting pyra rule

even more exciting , is that i've used it to generate a reference of every pyra library and example project ! go here to check it out in your browser . some of my favorites are the fizzbuzz and paint examples . you can click on the main heading of each page to see the original source file it was made from .

future plans include rule metadata and hyperlinked facts , but i'll be moving on to self-hosting pyra for now .

i also learned a few new CSS features and had fun applying them to my website , alongside a few other updates :

on another note , i had some thoughts about axowotw . it's been serving me well for managing side quests , but hasn't worked as well for the main quest . perhaps an additional skill tree interface would be more amenable to the ordered structure of the latter .

autumn jots this idea down as a side quest .

๐ŸŽง song of the day :
Safe Surface โ€” corru.works


december 10-25th

some friends died , so i'm taking a long break .


december 9th

today i started the documentation generator for pyra , which i'm also using as an opportunity to refine a representation of Nova rules for structural editors that i've been planning .

here's a screenshot of two rules from the painting example program , run through the docs generator :

the graphical representation of two pyra rules

and here's the original pyra code it corresponds to :

the textual representation of two pyra rules

some notes about the design :

i still need to incorporate representations for variables , strings , strides , and snippets . and possibly differentiate the causes from the effects even more .

finally , here's the compiler code that generates the HTML for this representation ( don't worry , the divs are just placeholders that'll be replaced with more accessible markup later on ) :

function formatPattern(pattern)
  if pattern.stack == ''
  then return format(
    '<div class="fact">_</div>',
    pattern.fact
  ) else return format(
    '<div class="stack">_</div><div class="fact">_</div>',
    pattern.stack, pattern.fact
  ) end
end

for _, rule in ipairs(rules) do
  iLine '<div class="rule">'
  if #rule.causes > 0 then
    iLine '<div class="causes">'
    for _, cause in ipairs(rule.causes) do
      local keep = ''
      if cause.keep then keep = ' keep' end
      local stride = ''
      if cause.stride then stride = ' stride' end
      iLine('<div class="pattern__">_</div>',
        keep, stride, formatPattern(cause))
    end
    iLine '</div>'
  end
  if #rule.effects > 0 then
    iLine '<div class="effects">'
    for i = #rule.effects, 1, -1 do
      iLine('<div class="pattern">_</div>',
        formatPattern(rule.effects[i]))
    end
    iLine '</div>'
  end
  iLine '</div>'
end

๐ŸŽง song of the day :
test & recognise [ Flume re-work ] slowed โ€” Seekae


december 8th

i finished more today than i expected ! it feels like the daily cognitive context-loading of December Adventure allowed me to venture deeper into the flow state than usual . changes :

tomorrow , it'll be time to start writing the library documentation generator , which i've been quite excited for !

๐ŸŽง song of the day :
ใ‚นใƒ†ใƒƒใƒ—ใƒใ‚คใ‚นใƒ†ใƒƒใƒ— โ€” Macroblank


december 7th

today i finished organizing the pyra libraries ! so refreshing . here are the libraries we've got now :

another thing that'll make the imports cleaner is to add a separate import type for standard libraries that lets you omit the path and extension . that means something like this :

~ include file:
  ../../libraries/one.nv
  ../../libraries/two.nv
  ../../libraries/three.nv

can be written like this :

~ use library: one two three

much shorter , and less fragile ! tomorrow i'll update the examples to use the new libraries in this way .

woah woah WOAH . hold up . you're tellin' me that the song of the day was concocted by autumn this time ? that's right :

๐ŸŽง song of the day :
waddle fractal โ€” autumn nebulae


december 6th

today i began restructuring pyra's libraries . i want to get this out of the way first , since it's the only significantly backward-incompatible change on the list .

the libraries that use snippets are currently organized by backend : lua.nv , ruby.nv , and javascript.nv . this results in a lot of duplication , makes it hard to split them up further , and just doesn't really make much sense .

the first step in ameliorating the situation is to merge them all into one , which we can do by allowing rules to have multiple snippets : one for each backend they support . now you'll be able to use the snippet symbol ^ followed by the name of a backend , and the compiler will insert the correct snippet depending on which program is using the rule . here's an example rule with a ^lua and a ^ruby snippet :

* get the current time
  ^lua f('result', {os.clock()})
  ^ruby f 'result', [Time.now.to_f]

i merged the backend-specific libraries using this new capability , so the next step will be to organize the rules based on the function they serve , like logging.nv or iteration.nv . after that , i'll need to update the example projects to use the new library scheme .

another motivation for restructuring the libraries is to prepare for the documentation backend . the compiler now stores which backends a given rule supports , so we can show this information in the documentation !

๐ŸŽง song of the day :
CLOCK โ€” Plaid


december 5th

today's been a quiet day of plotting and scheming ! here's the resulting list of priorities for pyra and beyond :

  1. restructure libraries around use , not compiler backend
  2. fix strings that contain quotes , brackets , and uppercase
  3. HTML docgen backend ร  la Hoogle
  4. self-hosted with Go backend for cross-platform binaries
  5. project creation commands
  6. stack changelog for debugging
  7. documentation on pyra.run
  8. create utopie using pyra with Godot backend
  9. create mooncake using utopie

a while ago i renamed the old version of pyra to "stackless pyra" , and "pyra0" to pyra . today i updated the name of the the repo to reflect this .

๐ŸŽง song of the day :
Hive โ€” Mobster


december 4th

i'm done working on my site for now , so it's finally .......

PYRA TIME !!! MUAHAHAHAHA

we're startin' off by gently vaporizing a terribly nettlesome quality of the pyra compiler : it doesn't produce deterministic output ! this becomes a hassle when you have a git repo with a pyra program , and the generated code changes randomly every time you recompile .

the code changes because Lua doesn't ensure an iteration order for tables , and i use Lua tables a lot in the compiler . today i fixed this by introducing a lovely little function by Rici Lake that lets you create ordered tables ! it stores the order that you add keys to the table in a linked list , and overrides the __pairs() iteration function to take that order into account :

local function Ordered()
  local key2val, nextkey, firstkey = {}, {}, {}
  nextkey[nextkey] = firstkey
  local function onext(self, key)
    while key ~= nil do
      key = nextkey[key]
      local val = self[key]
      if val ~= nil then return key, val end
    end
  end
  local selfmeta = firstkey
  selfmeta.__nextkey = nextkey
  function selfmeta:__newindex(key, val)
    rawset(self, key, val)
    if nextkey[key] == nil then
      nextkey[nextkey[nextkey]] = key
      nextkey[nextkey] = key
    end
  end
  function selfmeta:__pairs()
    return onext, self, firstkey
  end
  return setmetatable(key2val, selfmeta)
end

after adding this function , i only needed to swap out three tables in the compiler with ordered tables , et voilร  , deterministic output ! this also prepares me for making pyra self-hosted , because i can now use the example programs to conveniently verify that updates to the compiler don't change the output by running git diff .

on another note , i made few style fixes on a new website i'm helping to design for the Nouveau Community . Safari has always been the bane of my existence as a web designer . it seems like i'm always discovering new snippets of CSS the WebKit engine can't handle . this time it was text underlines ! (แ—’แ—ฃแ—•)

i still want to support WebKit browsers for websites i design , in spite of this . i had been using an old Apple device for testing , but today i realized i can install a browser for Linux that uses WebKit instead ! i'm on Fedora , so i installed GNOME Web using the command sudo dnf install epiphany . it worked splendidly for testing the design ; i should've done this a long time ago !

๐ŸŽง song of the day :
CHEAT CODES โ€” TOKYO MACHINE


december 3rd

the managing N24 post is finally complete ! today's portion dives into the VLiDACMel therapy , zeitgebers , and DSPD . it was difficult to include all of the information i wanted without teetering into overwhelming territory . hopefully i struck a good enough balance for people who are new to the subject .

i've been trying out a more casual and playful tone than i usually use in my "serious" informational posts . i noticed how much i've been holding back a more relaxed , and in my opinion , more authentic side of myself . the shift may not be apparent from the exterior , but it feels more nourishing . a small step toward the unfolding of the internet i long to inhabit .

finally , i sent an email to the creator of VLiDACMel , to thank them for how much they've helped .

๐ŸŽง song of the day :
81 Special [Slowed + Reverb] โ€” Caravan Palace


december 2nd

today i collected , ordered and structured all of the scattered notes i've taken about N24 into a friendly article-like shape . the first section is now live on the managing N24 page . i'm planning to edit and post the remaining sections tomorrow !

many of my notes were copied directly from written conversations i've had with friends , which has been a fun source to harvest from !


december 1st

the main project i'd like to work on this month is my programming language , pyra ! however , there's one page on my website that i want to finish first .

i have a rare circadian rhythm disorder called N24 . i recently learned how to manage the condition using a protocol called VLiDACMel , and have been writing an article about my experiences .

something i want to include is an actogram of the sleep data i captured between October of 2023 and November of 2024 . i used a sleep tracker called Plees Tracker , which is a great app , but there's no built-in way to create a visualization how i want . thankfully , it exports data as a CSV that looks like this :

12,1696711105697,1696744567977,4,
13,1696806609732,1696836850079,3,
14,1696902967015,1696940138010,2,
15,1697001200047,1697033781756,0,
...

the second and third columns are the important bit , and store the start and end times of each recorded rest period , encoded in milliseconds elapsed since the Unix epoch . not wanting to create a visualizer from scratch , i found a project that was close to what i needed and looked simple to modify . from there , i made a few modifications to the CSV parsing code and graph styling . the resulting script is available here !

processing my sleep data with the script results in the following graph ( click here to view larger ) :

a graph of my sleep data

this is actually the first time i've seen this data laid out so clearly , and it's interesting how chaotic it looks ! it almost resembles ISWRD , but i'm pretty sure that's an effect of having an unusually long phase delay and frequent interruptions .

today i also added an updates page , to list all of the other changes i've been making to my site recently !