Read the latest posts from The Blog of the EMMA Cooperative.

from andy

This is adapted from a talk given by Ramsey and Andy at NYU Game Center on April 28th, 2022. Thanks to Jane Friedhoff for helping us adapt it to a blog post.

What is a technology co-op? Why would you want to form one? And how on earth do you go about doing it?

In this post, we’ll introduce you to the core of EMMA: how we created it, how we operate it as a group, and what needs it’s helping us fill that non-co-op work didn’t give us.

But first, a few disclaimers:

  • Every co-op is different and special, and we can only speak to our own experiences. Your mileage may vary! We can only tell you what we’ve done, and the particular path we took to get here. Think of this not as a guide, but rather an example.
  • EMMA is a service co-op, not a product co-op. This is an important distinction: a product-oriented co-op (say, a videogame co-op) or a co-op in a materials-heavy industry like construction will likely require much larger overhead and startup costs before they can recoup their initial investment. As a service co-op, our overhead is very low and our startup costs were minimal. This, plus decades of individual experience with the creative coding market as a whole, gives us a lot of freedom that other businesses might not have.

With that out of the way, let’s get started!

What is EMMA?

EMMA is a state-recognized software consultancy co-op located in New York State, which went official in January 2022. We write software for clients on a contractual basis, usually in the interactive software industry (e.g. videogames, public installations, VR/AR, web experiences) but we’re willing to take on work wherever we have expertise. Depending on who you ask, the name is either an homage to Emma Goldman (an amazing anarchist of the early 20th century), or an acronym: Everybody Making Money and Art.

Operationally, we use a consensus governance model with a proportional compensation structure: that is, we all make the decisions together as equals, and we get paid relative to how much we work. We only have one class of member: there’s no difference between founders and folks who might join later. We also rotate responsibilities, and very actively stay in touch with each other to make sure everyone understands how everything works and feels empowered to act on the co-op’s behalf.

Fiscally, members keep 70% of whatever client money they bring in, with 30% going to EMMA to cover costs and future salaries. If a member brings in less than a pre-agreed “base salary” due to a dry spell or life circumstance, they get that base salary.

Legally, we are a New York State Domestic Cooperative Corporation, also known as a Worker Cooperative. While we resemble a more conventional corporation, we’re not an LLC or S-Corp, for reasons we’ll go into later. We are all simultaneously owners and employees, and the same will be true of future hires.

Spiritually, we’re a group of weirdos who dream of spending our time making art, not grinding work.


Before we go into the logistical details of running your own co-op, let’s talk about how the four of us gravitated towards the idea of starting one.

All four of us–Ramsey, Andy, Gwen, and Ivan–have been in the creative code and technology world in different capacities for decades. Andy, for example, co-led an indie games company, left that to teach game design, and landed in creative technology consulting. Ramsey had likewise been freelancing in creative technology since 2010, in addition to his games, art, and research practices. Gwen moved directly from university into traditional salaried employment in the creative technology industry. However, she found herself freelancing in 2020-21 as a consequence of covid making temporary work more readily available. Ivan has been working professionally as a creative coder since 2005. He has been freelance for most of this time, taking breaks to lead creative technology teams and teach game and graphics programming.

Although all of us came from different work backgrounds, we discovered we had all quietly been looking for the same thing. Many of us came from arts and indie videogames, and found the constant hustling those fields required to get even scraps of funding to be exhausting and draining. The COVID-19 crisis highlighted just how much control our various employers had over our lives and safety: those of us who were teaching, for example, were given baffling demands that we return to office during outbreaks or lose our jobs (and health insurance). Ramsey counts the October 17 Revolution in his native Lebanon as a pivotal moment that made it clear to him that a better world was both possible and worth building right now.

In our earliest discussions, we found that we all wanted basically the same handful of things:

  • The possibility of making enough money to live comfortably, while having enough time to make art (making art is great, but paying rent with art is exhausting).
  • The security of having a predictable salary (tricky for a job as boom/bust as freelancing).
  • The agency to work in a way that aligns with our values (instead of those of some executive board or shareholders).
  • The satisfaction of knowing our money and labor is building a structure that we value (and supporting people that we value).
  • The opportunity to do interesting work if possible (we reject actively harmful work, but are also happy to be plumbers for digital toilets).

Non-cooperative structures could have provided us some of these things (with much less legal hassle) but with a variety of tradeoffs we did not want to make. We were lucky enough to have been exposed to quite a few other co-ops, some run by people we knew personally. Feel Train, for example, was a creative tech co-op from 2015-2019, with co-owners Darius Kazemi and Courtney Stanton sharing their bylaws and documents to the public (We strongly encourage you to check those out!). KO_OP, Soft Not Weak, and Motion Twin are all co-op game studios that also influenced our decision to go co-op. These were enterprises run by people we respected, making work we loved, and watching them do so while exploring more radical modes of labor was a huge inspiration.

There are a few lenses on how EMMA works: our internal conceptual model for the co-op; the logistical model of the co-op; and how this model was encoded into the legal structures afforded to us by the state. Let’s go into each.

How EMMA works, conceptually

What values guide our processes about and within EMMA?

EMMA is a financial capacitor

As we said before, EMMA uses a proportional compensation structure, meaning we get paid relative to how much work we bring into the co-op.

We see EMMA as a kind of financial capacitor. A capacitor is an electronic device that can store a charge of electricity and discharge it slowly, over time. This consistency is welcome in a field like freelance software development, which has booms and busts: periods of lots of work, and periods of no work at all. The idea of EMMA is to pool resources–to metaphorically charge the capacitor–during booms so that we can pay salaries during busts.

image showing the effect of a capacitor on voltage

This interdependence is a welcome support network for an erratic field. On our own, the chance of any one of us not having work for a given season is reasonably high. But if we all come together, the odds that absolutely no one has work are low. The capacitor is always getting charged by someone, which provides security for everyone.

EMMA supports our art practices

As you might imagine, the fiscal and even psychological security of having a guaranteed income stream makes it a lot easier for our members to have the time, energy, and resources, to pursue our own art. All four of us have artistic practices, and that is not a coincidence. A top-level goal of EMMA is to support the non-marketable art that its members make, and free them from having to depend on selling art (and the concurrent market forces around art) to survive.

EMMA minimizes work

Although EMMA’s business model is creative code consulting, we do not dream of labor! We want to work enough to live comfortably, and then stop when possible. This seems obscene in a world that expects constant exponential growth–but working less is an explicit goal of our co-op, as we want our members to be able to pursue projects that they find valuable for their own sake, as we said above.

Knowing when to stop working/start turning down work can be hard when you’re on your own: even if you make a lot, there’s always a future fallow period to worry about. But within the co-op structure, we can set common financial goals and identify when it’s financially safe to turn down work.

EMMA is a small node in a (hopefully) bigger mesh

Although we don’t have an exact number, EMMA will always remain small–likely fewer than 8 people. The governance of the organization can really only work with a small number of members: consensus decision making often doesn’t scale well beyond a small group.

Furthermore, it is critical to us that all members know and respect each other–not in a general be-nice-to-each-other way, but in a way that requires everyone knowing each other well and being invested in each others’ success. This also functions best with a small group.

And just because EMMA is small doesn’t mean that its dreams are! Our dream isn’t that we run the One Giant Co-op Everyone Joins: rather, we’d love to see a future of many, many co-ops self-managing themselves, tailored to their respective members’ needs. In the ultimate version of this, we would see cooperative workplaces collaborating with each other by sending delegates to industry syndicates to discuss and solve industry-wide issues. These syndicates would then send delegates to regional federations, with power and decision-making rising from the bottom up. This isn’t some thing we’ve conjured up, similar structures can be found in classic anarcho-syndicalist and more contemporary democratic confederalist theories and is being put into practice in places like the Autonomous Administration of North and East Syria. Being small is a feature, not a bug.

How EMMA works, logistically

So how does EMMA actually function day-to-day?

EMMA is run by consensus governance

When we say “everyone decides” you might assume that we either vote on fixed proposals or require unanimous decision-making. In actuality, we do neither. We treat decision making as a conversation, where a given proposal often changes over the course of the conversation, and where responses may fall on a spectrum from “I love it” to “this is OK” to “I hate this and I’ll leave over it.”

This is an unusual way for a corporation to operate, but is actually pretty straightforward in practice, and works well with a small group like ours: as Ivan says, “it takes you longer to make decisions, but the quality of the decisions tends to be better.”

This isn’t totally foreign to the tech industry, either. The IETF, which writes and maintains many load-bearing protocols that hold up the internet, is deeply committed to what they call a “rough consensus” process that avoids the will of a majority eclipsing the needs of the minority. If you ever wonder if this consensus stuff could actually work at scale consider the fact that you’re reading this blog post delivered to you over several IETF protocols as evidence that it does!

Spoiler alert: lawyers did not like this! “Infinite growth” is basically an axiom of modern business: all the structures around making one assume that the goal is to just keep scaling up forever. But our consensus governance model doesn’t scale all that well–doing it with four people is a lot easier than trying to do it with four hundred. We had a lot of back and forth with our lawyers that we understood and did not mind this!

EMMA uses proportional compensation

You also might assume that everyone is paid the same amount at EMMA, and this is not the case either. Everyone is paid the same way, but not necessarily the same amount.

Some co-ops do make it a point to pay everyone the same amount. It’s a perfectly valid approach, and we considered it at first, but we realized that it was not a good fit for EMMA. After all, different people have different life needs. Some may be happy to couch-surf, while others may be itching to buy a home. Some may have zero dependents, while others need to take care of family. Some may be keen to work absolutely as little as possible, while others may want to work more and build out their emergency funds. Additionally members will almost certainly wind up moving within that spectrum, and possibly in a big hurry! Having just one rate for everyone bulldozes over these various individual considerations and desires.

Rather, at EMMA, everyone is paid with the same method. Everyone keeps 70% of whatever they bring in, with 30% going to EMMA to cover operating costs and future salaries. In addition to that, as a safety net, if someone has a rough month and brings in less than a collectively agreed on base salary, no problem: they get paid the base salary anyway, thanks to our financial capacitor. This covers both dry spells and slow client payments. This structure allows people to work different amounts and come away with different amounts, which helps us support members regardless of their current life situation.

However, being paid different amounts doesn’t affect our governance. After all, a company could have a flat pay structure, while having an operational hierarchy. We have the opposite: while we make different amounts of money, each member always has the same decision making power within the co-op.

As a co-op we have access to certain kinds of corporate profit accounting that other legal entities do not. Specifically, we have at our disposal mechanisms to share EMMA’s end of year profit with members in novel ways. We have not enacted any of these mechanisms, however, as we have not been in business for more than a year. As of now the profit sharing plan is basically a spill-over. if 30% of EMMA's revenue is more money than we actually want to keep in the co-op we can use profit sharing to distribute that cash back to the members.

EMMA has one class of member, and everyone does everything

At EMMA, we have exactly one class of worker-owner: the “member.” There is no hierarchy among members, and when other members join down the line, they will be exactly equal to the founding members following a candidacy period.

It’s worth noting that not all co-ops are like this: it is not a legal requirement. The principles just call for “democratic member control,” which could allow for hierarchy. (Some larger co-ops have, for example, boards of directors and managers forming a more traditional looking corporate hierarchy. These positions are chosen democratically by the worker-owners, however). Given our small size and the political inclinations of some of our members we opted for a flat governance structure.

In addition to not having a hierarchy, we don’t want our members to get siloed into operational roles: we want everyone to do everything, at least a little. A practical example is our books. Every member of EMMA is trained to maintain the company’s books, and updates the relevant ledgers to reflect their own client work as well as general company transactions. Another is our payroll system: we rotate who actually operates the software and moves the money around, while the rest of us sit in on a video call to provide guidance and a double-check work.

This system of shared responsibilities has a lot of benefits. First and most pragmatically, this avoids the “hit by a bus” problem: if Ramsey is the only one who knows how to do the payroll software, but he gets hit by a bus, how will EMMA pay its employees in his absence? Much better to spread that knowledge out so that everyone feels competent at taking on a given role within the organization.

Second, this system makes our worker democracy more meaningful. Micheal Albert makes the argument that traditional division of labor in a cooperative runs the risk of eroding the collective’s democracy and creating a “coordinator class” of people who know how to run the enterprise at a higher level distinct from everyone else. For example, even if Ramsey didn’t get hit by a bus in the previous example, if he’s the only one who knows how payroll works, how can the co-op discuss payroll-related issues as equals? So we make it a point to share knowledge and make sure everyone gets their hands dirty in all aspects of running the ship.

How EMMA works, legally

How do we make sure the state isn’t mad at us?

First of all, we should disclaim yet again that there are many ways to approach incorporating a co-op. Some of our decisions were made to best fit our specific needs; some were made because we had certain freedoms that gave us more options (like having cash on hand to get set up); and some were likely mistakes we made because we didn’t know any better at the time! Talk to us in a few years when we have the benefit of hindsight.

All this just to say: the following isn’t a guide, and it is most definitely not legal advice, just an example of how we did things.

EMMA is a NYS Domestic Cooperative Corporation (Worker Cooperative)

On a technical level, we are a NYS Domestic Cooperative Corporation, or a Worker Cooperative. NYS has a legal entity that fits our needs and values so we went for it – this will be different in different states! We resemble a corporation, in that EMMA is a “legal person” that is taxed separately from us, but one where we are all owners and employees. We are compensated for our labor throughout the year and share in profits at the end of the year (there are co-op-specific mechanisms for this, although we have not had a full financial year to actually test any of them).

As a state-recognized co-op, we get some special benefits, as well as a handful of restraints that we actually really like.

New York State law requires us to act and govern ourselves as a co-op. This is both a restriction and a perk! This requirement makes it difficult to “stop being a co-op” if future members try and do that. This happens enough in co-ops that it has a name: demutualization. In S-corps and other similar entities that are set up to be co-ops via their bylaws alone, it is possible for the members to vote to demutualize at a later date. We want to make that as hard as possible.

And, of course, we get to legally be called a cooperative in our company name and get a .coop URL, which is pretty neat.

The main downside is the added complexity of setting up this corporate structure. Online incorporation platforms like LegalZoom do not have “cooperative” as an option, so you will need to engage some lawyers to draft and file your paperwork for you. There’s no reason a “LegalZoom for Cooperatives” could not exist, but figuring that out is an exercise left to the reader.

EMMA “totally has a hierarchy” ;) ;)

Remember our whole “no hierarchy” thing? Turns out that NYS requires every corporation to have a president, vice president, and treasurer. Boo! But rather than tangle with the law, we decided to get around this requirement by simply stripping those positions of any and all unique powers in our bylaws, and choosing them by dice roll. We will rotate positions each year, but we made sure that legally, they did not matter.

As part of our process, we also had to explicitly request one class of member, one class of stock, and so on. Our bylaws require all employees, owners, managers, officers, directors, shareholders, etc. to all be the same ‘person.’ Likewise, there is only one class of stock, and any member can only own a single share. Most of this was outside the default, and not obvious to the state/lawyers, so we had to explicitly request it.

How EMMA pays taxes

This part is speculative because we haven’t been incorporated for a full year at the time of this writing, but we intend to file our end of year corporate taxes as what’s called a “T-Corporation.” This is a corporation that files under the rules in subchapter T of the IRS code (the subchapter right after subchapter S that gives S-Corporations their name!) and files form 1120-C as opposed to the 1120 that conventional corporations file. Subchapter T and 1120-C are the cooperative specific tax rules we are subject to.

There are a few bells and whistles here in terms of how we’re allowed to retain and share profits, but given that we haven’t gotten this far yet we can’t really comment deeply on any of it. In theory, we can do a kind of end of year accounting unique to co-ops that might allow members to keep more of the money they made during the year compared to conventional forms of compensation. Also, we can more easily use EMMA as a “cash warehouse” meaning we can retain an unbounded amount of cash at the company without incurring extra taxes. This is important when we’re trying to keep that capacitor charged! Again, given that we have not been open long enough as of the time of this writing to have crossed a tax year boundary, this part of the structure is still speculative. Check back in with us in a year!

EMMA uses professionals to help manage all this

There is no way we could have done all this on our own. The process of incorporating and maintaining our money requires people with expertise we simply don’t have!

By and large, we tried to stick with organizations that aligned in some way with our values. This mostly meant working with other co-ops. There was typically a trade-off in convenience and accessibility, but it was nice to work with folks who not only shared our political goals, but who also tended to have a better understanding of what a co-op even is.

Our legal counsel was provided by JL Weiner & Associates whom we liked working with, and would recommend. We bank with Brooklyn Co-op FCU, got bookkeeping guidance from A Bookkeeping Cooperative, and used Gusto to run payroll.

Benefits we get of being part of a co-op

We’ve already mentioned some clear benefits of being part of a co-op–the financial capacitor aspect being a key one. For a while, it seemed like that was the primary thing we’d get out of it. And don’t get us wrong–that’s plenty great on its own! But even long before we were officially incorporated, we started seeing a lot of other benefits too.

It provides mutual support and a framework for knowledge-sharing

Work is hard! Freelancing involves a lot of interpersonal and operational skills that can be overwhelming: everything from doing taxes to dealing with stubborn clients to negotiating rates. And if you work in a field where things are constantly changing–like ours, where new technology is getting introduced, frameworks are getting changed, and software updates are constantly wrecking past projects–it can be very stressful to take on a new project solo. Having a team of people away from clients to lean on when things are confusing is invaluable.

This mutual support really comes in handy when we run into the kinds of technical issues mentioned above. We’re all creative coders, but even so, we all have different skills and specialties. Each of our members is an expert in at least one thing that the others know very little about. Our tech support channel in our self-hosted MatterMost (an open source Slack clone) is a wildly helpful resource whenever any of us run into issues on the job.

Furthermore, this knowledge-sharing doesn’t end when the gig does. While we come to each other to solve specific problems, we also find that the co-op structure encourages genuine ongoing learning and mentorship.

It makes it easy to share gigs

If you’re freelancing during a boom period, you may get solicited for more work than you can take on. It takes a bit of time and effort to reply to a potential client with a curated list of other people who might be able to do the gig well. But with a co-op structure in place, it’s as easy as saying, “I am booked, but let me check with my cooperative!” and passing the work on to them. Indeed, we’ve already shared gigs and bookings (at the time of this writing, we’re all booked on the same client!). And this added income supports all of us in the end.

This sets us up as a kind of stable of experts who can be brought on for a client’s project as needed. Ivan compares it to going to your favorite barber shop as a walk-in: your absolute favorite barber might be busy, but you know that everyone working there is good.

It gives us better negotiating power

A lot of freelancing stress comes from money (surprise!). As individuals, we’ve all needed and given advice to others who are struggling to set their rates, or trying to write an email gently reminding a sluggish client to pay.

But as a co-op, it’s easier to ask for money, and easier to get that money. We’ve already used ‘’ to remind clients of a late invoice to very positive effect. This faceless collective email account helps avoid the discomfort of personal client confrontation.

Further, we are able to bounce rates off each other, and treat rates as standard across our co-op. This is doubly important given the many pay inequality issues in tech (across race, gender, etc.). When we work separately, and when wage information is effectively secret, it’s easy for clients to nickel and dime us individually. But when we share our rates and bargain together, we can provide a unified front for more fair compensation for everyone. A rising tide lifts all boats!

Advice for starting your own co-op

Hopefully, we’ve given you some practical insights into how you might conceive of, form, and run your own co-op. But before we sign off, a few pieces of advice:

Co-op structures do not guarantee success

Hopefully obvious, but being a co-op does not on its own guarantee success. EMMA is doing well right now, but that’s in part thanks to the fact that we were all doing well as freelancers before EMMA. We were able to bring professional networks built over decades to the co-op, and that’s certainly bolstered our current business. We don’t want to give the impression that adopting a cooperative structure is a guaranteed recipe for financial success! You still have to succeed as a business in conventional ways.

You can test drive a co-op today!

Before we were a state-sanctioned cooperative corporation, we spent about a year unofficially acting like one. We had weekly meetings hosted on a friend’s personal Slack, where we’d talk about work and hold each other accountable to our various career and artistic goals. This regular communication made it easy and convenient to share gigs, give advice, and help with rate negotiation, allowing us to build solidarity by lifting each other up. A year of doing this gave us a strong working relationship, and an investment in each other’s success and happiness long before any paperwork was filed.

The nice thing about this is that you can do it today, no government forms required! If you’re interested in starting a co-op, try building a habit of normal meetings with your desired collaborators, and creating platforms where you can share your advice/expertise/networks with each other. Even if you don’t end up starting a co-op, you’ll likely find that you get a lot of benefits having this space to work together rather than apart.

Say hi!

We hope this was helpful to you! If you want to see what we’re up to you can check out (love that .coop domain) or follow us on social media:


When we gave this talk at NYU, we closed by linking to these resources that were mentioned in the lecture. They helped us and hopefully they can help you too! — Democracy at Work, resources on forming and running a co-op — Feel Train documents


from andy

Earlier this week I released Pico-Pond, a networked PICO-8 demake of Frog Chorus, a web-project I love by v buckenham and Viviane Schwarz.

You can play Pico-Pond for free in your browser here:

You can also look through the source code, which I hope will be helpful for anybody else looking to make a networked PICO-8 game. The code is commented and (hopefully) easily readable.

Please keep in mind that this was a weekend project that was never meant to scale beyond 20 players. I did not try to make it super efficient or account for every edge case.


screenshot from the game

Just like in the original, you control a single frog in a pond populated by other real people. All you can do is hold a button to grow in size and then release it to make a ribbit. The ribbit is louder and longer the bigger your frog is when you release. All credit for this design goes to buckenham and Schwarz. It is deceptively simple, but the act of communicating with strangers this way is surprisingly rich.

What makes Pico-Pond somewhat unusual for PICO-8 is that it is networked! This is not a standard feature of PICO-8, but is possible in a web build through some fairly straightforward hacks. I've been curious about exploring this for a while after reading about some early work in this direction. I selected a Frog Chorus tribute both because I loved the original and because the simplicity of it lent itself to a good first project.

Before I get into the details, here's an overview of how this project connects multiple users into the same PICO-8 experience. The magic is in the gpio pins added in version 0.1.7. The gpio pins represent a set of 128 bytes of memory and were designed for PICO-8 projects on the raspi to allow games to be able to respond to hardware on the device. For whatever reason, they are also accessible to JavaScript running alongside the game in web builds. This can bridge communication between the PICO-8 game and the surrounding web page. Once you've broken out into JS, the sky is the limit.

This particular project uses a setup like this: The gpio pins are used to allow my JS and the PICO-8 game to communicate. My frontend JS then communicates to a node server hosted on Heroku using a websocket.

flow diagram

JS / PICO-8 Communication

The first time I read about doing something like this was a few years ago when I stumbled across this post by edo999. This used JavaScript equivalents of the PICO-8 peek & poke functions to insert or read values from the heap. Seemed promising and I kept it in my back pocket as something I wanted to check out. More recently I encountered seleb's post about a functional twitter client he wrote in PICO-8 (it's really cool. Check it out!). He was using the gpio method and was kind enough to include his source code.

This method is very easy! For the JS file, all you need to do is create an array of 128 elements called pico8_gpio. After exporting the PICO-8 game as a web page, the javascript file needs to be included in the HTML file. The 128 elements of this array map to 128 locations in the PICO-8 memory starting at location 0x5f80. To write to slot 2 and read from slot 5 in JavaScript I might write

pico8_gpio[2] = 32;
let my_val = pico8_gpio[5];

And to do the same in PICO-8 I would use peek & poke to manipulate memory

poke(0x5f80+2, 32)
local my_val = peek(0x5f80+5)

That's pretty much it! One tricky thing is that this only works when running a web build, so testing in the editor becomes tricky. I wound up writing some testing values to memory in _init() so that my app thought it was connected. Needing to make a web build definitely increases debug time.

Managing Communication

There are 128 gpio pins to use! As seleb notes in his post, “128 bytes is a pretty big chunk of data for a PICO-8 app.” Pico-Pond doesn't even come close to using all of them. Seleb came up with a clever structure to allow the JS and PICO-8 elements use the same pins in order to send large amount of data, with the first pin acting as a control pin letting both apps know whose turn it was. Luckily my needs were simpler so instead every pin is designated as being for a specific app. Either the JS script writes to it and PICO-8 reads, or the other way around. They never write to the same pin.

One note is that although I could store negative numbers in these addresses from PICO-8, I was not able to write a negative value from JS. So some things might seem a little odd (like using 200 to mean that a frog is not active). I'm sure this can easily be fixed, but it was never enough of an issue for this game.

Here's the breakdown of pins in the finished project. I didn't settle on 20 frogs right away and I wasn't sure how many pins I would need for each frog, so I left the first 100 pins for game-state information from the backend and started additional data at pin 100.

PIN number Description Who writes to it Use
1-20 Frog Values JS values of all frogs in range from 0-100. 200 means that frog is unused
21-99 unused unused unused
100 Player Input PICO-8 1 if button held, 0 if not
101 ID JS ID of this player. Set by backend
102 Cart Start PICO-8 starts at 0, set to 1 when cart is loaded to let JS know it's ready
103 Status JS Tells the game the status of the connection to the backend (waiting, connected, room full, disconnected/error)

Pin 0 wound up being unused when I moved all the frog values up by 1 pin to match PICO-8/Lua style of indexing arrays at 1 (a constant source of consternation).

The values in 1-20 and 101 get set from the backend, but not directly. The JS script communicates with the backend via the websocket. There is a regular pulse from the backend with the gamestate. When the frontend receives this message, it writes the values to those 20 pins. Likewise, when the backend responds to a new player with a frog ID, the frontend JS writes that ID to pin 101.

The JS script does not manage game logic at all. It just acts as a bridge between PICO-8 and the backend.

Keeping the Data Small

You might notice that each frog in the game is stored as a single number between 0 and 100 (with 200 meaning they are inactive). Although I didn't think I would even come close to using all 128 pins, I wanted to keep the amount of data moving from the backend as small as possible (mostly so that I could easily send the entire gamestate instead of needing to have a more clever system).

One way to approach a game like this would be to have the backend create a frog when a user connects. Things like the X and Y, the color etc of the frog could be generated and stored in the backend. This would be fine, but then all of those attributes need to be sent to clients who connect to the game. In this game the only thing that changes is the size of the frog (increasing when the button is held and then returning to 0 when released), so I wanted that to be the only value managed by the backend. I think an argument could be made for just having the backend track if a player is holding down the button or not and letting the PICO-8 game manage the rising and falling value, but I wanted the backend to have an accurate snapshot of the current gamestate so that a newly connected player would have all of the correct data.

Right now, when a new player joins, the backend finds a random open frog and sends them the ID of that frog. The frog object on the backend consists of a number value and a boolean for if the player is holding the button. Every tick (targeting 30FPS to match PICO-8) the frog value goes up if the button is held and down if it is released. Changes in the user's button state are sent via websocket whenever the frontend JS sees that the input pin has changed value.

So what happens to those other attributes? They don't really have to be consistent; they could be randomized when the game starts, but that doesn't feel right. If I'm the red frog in the bottom left corner, it feels like I should be that frog for everybody.

The easily solution here is to randomize these things, but to seed the random number generator so that the values are the same for everybody. I wrote a simple (and very inefficient (please don't @ me if you read the source code)) function to randomly distribute the positions of the frogs to roughly fill out the screen and then randomized the other frog attributes (color, sound etc). For each of these steps I tried out a few different seeds until I found ones I like. Now all instances of the game have the same pond without needing to send those extra values across the wire.


The last thing I needed to do was make the frogs ribbit. One of the charming things about Frog Chorus is that the different frogs have different voices (audio recordings of people saying “ribbit”) that get played at different speeds and volumes depending on how big the frog is when you release. The fact that the frogs sound different from each other and that the sound is modulated by how long the button was held contribute in a big way to the conversational feel of Frog Chorus. You can get into a groove with people where you do lots of little chirps or each grow really big to do a loud ribbit at the same time; it's a lot of fun! I wanted to make sure that this aspect was captured as well.

As such, one-shot sound effects were probably not going to cover it. Luckily there are some tools I know from making hyper-condensed tweet jam games that allow for some dynamic audio generation. Weirdly, the print command can be fed a special character that causes the string that follows to be played as audio. This whole portion of the PICO-8 documentation on control codes is pretty interesting.

The basic format is “\ab..e#..b” where “\a” is the control code that signals to PICO-8 to treat the string as audio and then everything after that is telling it what to play—in this case a B and E# and another B with a slight pause in between each note.

There are some additional properties available such as volume (v), instrument (i), effect (x), and speed (s).

For example let's look at “\as9v7x3i0f..d-..f”. Going from the end and working our way to the left this string plays an F note followed by D flat then another F (“f..d-..f”) using instrument 0 (“i0”), effect 3 (“x3”) at volume 7 (“v7”) and speed 9 (“s9”).

Not very pretty to look at but it gives a lot of control.

Speed and volume are fantastic for modulating the sound. I mapped the size of the frog when the button is released to these values so that big frogs play a slow, loud sound and little frogs play quick fast ones. After some trial and error, I got it so the length of the sound roughly matches the length of time it takes the frog to shrink back down to the base size.

I'm sure a sound effects person could do a better job, but I got a fairly nice ribbit-y trill by going back and forth between two notes like this: high note, low note, high note, low note. These notes are defined for each frog in the setup function using the same seeded random function as the other attributes.

Then, to give frogs their own voice, I also randomize the instruments and effects. Not all of them sound froggy, but I tried each one and made a pool of acceptable options for each frog to pull from in setup.

If there are tons of frogs in the pond, these sounds are likely to get cutoff when multiple frogs use the same instrument. I think that's OK though because that means there are enough people using it that it will feel lively anyway.

Wrap Up

There's a bit more going on, but those are the exciting parts! Please dig into the code if you're curious. Once I was finished I went over it and commented everything to try and make it as easy as possible to parse.

I hope this is helpful if you want to add a bit of networking to your own PICO-8 projects!

Frog Chorus was made by v buckenham & Viviane Schwarz.

The frog pic used in the game is a lightly modified version of an image made by scofanogd and released with a CC0 license.

Thanks to Sean S. LeBlanc for the breakdown of his PICO-8 twitter client, which was a key part of getting this off the ground!

Thanks to lexaloffle for PICO-8!


from gwen

I'm gonna start this blog with a softball I happened upon in Stack Overflow today. This snippet will give a user on a linux system password-free sudo permission but for a specific command(s).

In my case I want a little side project to be able to reload nginx's config but I very much do not want a hacky side project running with complete sudo access. This snippet is perfect as I can allow that user sudo access but only for this one task!


Add a line like this to /etc/sudoers or /etc/sudoers.d/{user}

username ALL=(ALL) NOPASSWD: /path/to/command arguments, /path/to/another/command

Now log in as that user either via ssh or sudo su {user} and try your command.

$ systemctl reload nginx


This will allow the user gwen to reload nginx but nothing else

gwen ALL=(ALL) NOPASSWD: /usr/bin/systemctl reload nginx

And this will allow the user gwen to reload or restart nginx but nothing else

gwen ALL=(ALL) NOPASSWD: /usr/bin/systemctl reload nginx, /usr/bin/systemctl restart nginx

Credit for the solution goes to the original stack overflow post!

#sudo #linux #sysadmin