Table of Contents

February 2024 newsletter

What we've been up to

Demos

(Omar: Both Forrest and Vedran's demos [and the receipt printer work from last year] indicate the potential importance of custom regions and accurate calibration, instead of the hard-coded 'half-size page' that we currently have. Ideally, it would be easy to make the program region follow the outline of the hex tile or the train or whatever else.)

(I always like when there are specific physical objects, like Folk being overlaid on a book, or a toy train track, etc, instead of a featureless table. The train track in particular has a nice inherently three-dimensional character, it's not like your hand just lifting planar stuff above the table for a second. This all also motivates some of the RFID / alternative tracking, seeing how much the tags stick out visually and how much space they occupy.)

Template matching

Andrés: I've spent most of this month working on template matching (finding the location of an image in a larger image) to enable tracking custom game pieces in Folk. I wanted a basic demo where you can:

  1. capture a small, square image from the table, this is known as a template.
  2. run a program that continuously searches for the template and highlight its location

To that end, I wrapped a few programs into this “Image Slice Matcher” folder made from cardstock:

templatefoldercover.jpeg

templatefolderinside.jpeg

When you open the folder, the left side includes two programs: on the full-size page is a program titled “slice” that will show if you've captured a template image, plus a program that pulls out to the side allowing you to trigger the template capture. Like so:

In a previous version, you had to manually take a photo of the table in order to recognize a template. Now the program reads the global camera image from Folk's main process. This allows the program that looks for and highlights matches to operate much faster and work continuously. Here it is searching for the cover of a small zine as I move it around the desk:

Next month, I'll be extending this to track game pieces in a booklet of games I've been working on.

Parallel evaluator

Omar: I've had some downtime this month, so I've mostly been continuing to work on the parallel evaluator (see also last month).


I've been saying that the pattern is that every 5 months or so, Folk starts feeling too slow, so I rewrite the Folk runtime; then it's way faster, so we pile more stuff onto it over the following 5 months, making Folk slow again, which leads me to rewrite the runtime again. (These runtime rewrites are usually surprisingly backwards-compatible and keep all the language and user programs and hardware interface intact.) This has happened like 3 or 4 times by now, including changes like:

We have reached this point again; I'm regularly frustrated with how slow things feel on folk0 when more than 1-2 things are on the table, even though low-load performance is still pretty good. (I think a lot of people's home systems don't suffer from this as much because they don't have random stuff out, they're working on one thing at a time, and they're mostly using it on their own, not showing it off.)

But I'm hoping that this rewrite will be a permanent improvement where each program doesn't have to make latency worse at the margin, once we have scheduling and preemptive multitasking – we'll be much more like a real operating system where the marginal program doesn't slow down the rest of the programs.


In the new evaluator, I've been introducing a lot of the breaking changes that we've wanted to introduce for a while. These are only in the separate new repo right now, but we fully expect them to come into mainline Folk once the new evaluator is ready:


Example of how you'll be able to write programs/Whens that run concurrently, with arbitrary blocking & loops:

This is useful, for example, for talking to webcams: you can have a virtual program that is a simple infinite loop, where the body blocks on the webcam read from Linux, then Hold!s a statement with the resulting camera frame. Or for making HTTP requests: you can write a program that literally does exec curl and then uses the result without halting the rest of Folk. I really like that naive straight-line imperative code will just work and do the reasonable and expected thing.

Also notice how the other thread gets arbitrary samples of the counter value (0, 97, 107, etc), it doesn't actually evaluate for every intermediate state. This is the behavior that you want for statements! If processing takes a while, you don't want to fall behind by processing intermediate page locations or whatever as the user moves a page around, you want to catch up to the latest stuff as soon as possible.

(If you _do_ want to process intermediate states, you want something with different semantics from normal statements, like event statements or something. This is an open question.)


(under the new evaluator, I'm hoping you can just beam stuff out to the GPU at 60fps, using Query! to sample from whatever happens to be in the database at that moment; you don't need to wait for a system convergence that includes all programs)


Ported the web server, which (elegantly) can now just be a normal virtual program, since it blocking on clients/sockets is perfectly OK in the new evaluator.

Ported most virtual programs (camera, AprilTag, GPU/display). Also elegant: finally trashing the pi/ directory and inlining everything (talking to GPU, cameras, other libraries) into the virtual programs. (We want to do this partly to make everything live-editable inside the system; it should help with readability/understandability, too, since you'll know exactly what files to read to understand those subsystems, and there'll be fewer intimidating random folders/files in the repo.)

Even mostly works on my laptop (because we're no longer reliant on fork which completely breaks GPU access on macOS):


The key issue with the parallel evaluator right now is the locking, which is increasingly messy. (the naive one-big-lock implementation from last month ended up not working at all, deadlocks everywhere). So I've been trying to figure out what data structures and invariants we actually need…

img_6042.jpeg img_6095.jpeg

Did a pass of looking at the data structures (Statement and Match) and ripping out stuff, especially reverse (child→parent) pointers.

matchstmt.jpg img_6167.jpg

(I think we can get rid of a lot of reverse pointers by 1. using a flat reference count instead of list of reverse pointers [flat reference count is also just a lot cleaner to deal with in C as a value, you can make it atomic] and 2. leaning on weak refs more and just letting the forward pointers get invalidated and leaving them dangling instead of needing to use reverse pointers to walk back and clean them up)



Next steps here are to figure out the data structures/locking/atomics so that tests reliably pass, then do a run on a table with all the major components of the system (camera, AprilTags, GPU) and see how it performs compared to the old evaluator (that end-to-end test is really the only way to find out, I think)

RFID

Omar: RFID work continues, on and off – we left off in January just about being able to do multiple hops on IB and wanting to continue on to OOB and live reporting and localization.

Unfortunately, this month I found that IB is still too unreliable (it breaks down after the first round for some reason). so it can continue for multiple hops, but not _that_ many hops, which means it's not feasible yet to leave it on and sync the OOB to it and do the whole live localization process. See how it breaks after 1-2 rounds (“THE TOP”) in these examples:

and

Anyway, in February:

There must be some bit of hidden state in the IB radio that causes it to go off the rails and consistently fail after that 1-2 rounds… fix that & fix error recovery, then set up OOB(s), then set up localization.

Friends and outreach

Other work

What we'll be up to in March

Omar

Andrés