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 :
- it creates a record of past autumn's plans and reasoning . my memory isn't that good , so this has been helpful even within the span of a month
- daily logs are more detailed and entangled than weekly logs , but more refined and focused than hourly logs
- it's satisfying and motivating to see the amount of progress i'm making , laid out clearly
- transparency . i've always fancied the idea of open sourcing my mind , and this is a step toward that end
- iokodobaba . "have a plan. work on your big project at least 30 minutes a day. even if it's just planning" . it adds up !
here's a recap of the most notable things i did this month :
- pyra updates :
- deterministic compiler output
- restructured libraries around use case
- fixed strings
- new library import method
- transitive dependencies
- backend annotations
- HTML docgen backend
- online library reference
- bootstrapped self-hosting
- wrote managing N24
- redesigned plokta.xyz
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 :
no carets for you !the massive snippet snarled at me , so i added a backslash escape option- the global variables and functions required extraction into a global snippet
- then i deduced that the
stacks,rules, anduniqueFirstStacksglobals in the compiler clobbered variables by the same name in the compiled output . that was confusing to figure out ๐ตโ๐ซ
๐ง 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 :
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 ?
- the ability to compile the compiler itself to any backend target ! i'm interested in three things this will allow :
- creating cross-platform static binaries using a Go backend . the current installation process isn't beginner friendly enough for my taste
- putting pyra on the web , for educational purposes . i'd like to make something similar to Gleam's lovely language tour
- parsing pyra files in utopie , which will run on GDScript
- a more literate and understandable compiler
- identifying weaknesses in the language and libraries
- it sounds fun !
here's the plan of attack :
- throw
pyra.luainto one massive rule snippet , and compile it using the Lua backend - recursively split up the snippet
- extend this process to each backend
- once snippets have fully receded into the realm of standard libraries , it'll be self-hosted enough for my purposes !
- 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 :
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 :
- roses origami ๐น
- added a portals section to the wiki
- enabled
text-wrap: prettyfor paragraphs - made the header and footer more stylish using CSS animations
- added a theme picker that doesn't rely on JavaScript , except for persistence
- improved the light mode palette
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 :
and here's the original pyra code it corresponds to :
some notes about the design :
- each rule looks like its own "note card"
- the causes have white text , while the effects have pink text . there's also a heavy drop shadow to make the line between them apparent
- facts that will be kept have a green line on the left side
- the stack and fact pairs are separated using vertical lines , which correspond to colons in the textual representation
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 :
- updated the pyra examples to use the new library and importing scheme
- added support for transitive dependencies , with deduplication
- resisted the urge to improve the examples and library code . this was difficult , but it's low priority and i'd rather spend my time preparing for documentation right now
- added a script to rebuild every example at once
- fixed strings . double quotes are automatically escaped . you can manually escape closing brackets . you also need to manually escape strings that start with a capital . this is an unusual language design decision , but it's the only thing that meshed well with capitalized variables
- pushed the changes to the pyra repo , so you can try them out yourself !
- added myself to the team page on the Nova website
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 :
- browser
- case
- comparison
- dom
- filesystem
- inline
- iteration
- love
- math
- nokogiri
- stacks
- strings
- system
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 :
- restructure libraries around use , not compiler backend
- fix strings that contain quotes , brackets , and uppercase
- HTML docgen backend ร la Hoogle
- self-hosted with Go backend for cross-platform binaries
- project creation commands
- stack changelog for debugging
- documentation on pyra.run
- create utopie using pyra with Godot backend
- 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 ) :
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 !