• Last week I got my Panic Playdate in the mail. I preordered it last July, and have been following it since the May 2019 EDGE announcement, so you could say it was a long time coming. It’s such a nice feeling device, and good looking, too. Sure, the non-backlit screen can be annoying, but I firmly hold the opinion that portable gaming is pretty stupid, so I’m not jumping on the bandwagon of acting like no backlight is some sort of extreme deal breaker. I was looking forward to building things for it, and the general feel and look of the thing just makes me extra excited.

    My last stint of game development aspirations were cut a little short due to PICO-8’s code volume limitations. I hadn’t actually run up against those limits yet, but getting close to them filled me with so much anxiety and dread that I just couldn’t keep going–it was clear there was never going to be enough room for me to build the game I truly wanted to make. So, it stopped being fun, and I quit.

    Playdate development promises to be less anxiety inducing. There is no limit on how much code you can write, beyond the physical limitations of the device itself, which is very generous in regards to RAM and storage space. There are new restrictions, though: unlike the 16 colors available in PICO-8, the Playdate has only two colors: black-ish and gray-ish (technically black and white, but on the device both colors are more silvery). This presents an extremely interesting challenge for someone like me, who has no confidence whatsoever in his artistic abilities.

    I decided to start with what I know, and built a spaceship flying around, similar to my PICO-8 game, but also significantly different.

    I’m using some icons from The Noun Project (disclaimer: I work there) that I quickly redrew in Aseprite (to varying degrees of success).

    Another new challenge is that because the screen is so small, high resolution, and not backlit on the physical Playdate, you have to make your sprites pretty large to make them legible. The Playdate simulator inadvertently downplays this issue by virtue of being so large on your screen (Panic should create an option to remedy this). This is a big change from PICO-8’s gigantic chunky pixels, where a single pixel can be perfectly visible to the player.

    Because of that, the way my space game worked on PICO-8, where every ship was a single pixel flying around a large star map you could freely explore, simply won’t work on the Playdate. It’s forced me to think of a different way to adapt the general gameplay loop, and I think I’ve come up with an idea I like and will pursue. I won’t talk about it yet, we’ll see if I build it.

    I wanted to spend some time contemplating the aforementioned game idea, but wanted to keep building. So I decided I’d try porting my MultiClock screensaver over to Playdate. Figured it would be a fun programming challenge, and I was right!

    The biggest difference is that SpriteKit comes with performant methods for rotating sprites in real time, part of its animation system. You can rotate sprites in code with Playdate, but it is processor intensive so they do not recommend doing so. So instead I had to draw the clock hands in Aseprite, something I’d never used before, and create each frame for the hands rotation. In the above video, we’re up to 32 frames, almost enough to make the rotation look pretty smooth. Maybe not yet as smooth as I’d like, but the next jump is from 32 to 64 frames and that’s going to be a bit tedious.

    Right now I really just have the time display working, none of the animation system or alternate patterns from the screensaver have made it in. I am pretty sure I’ll be able to build the animation system out similar to how it works in the screensaver, with a queue and callbacks and so on.

    I figure this’ll look nice when and if the Playdate Stereo Dock comes out some day.

    Oh, also, I bought Panic’s new code editor, Nova, to use for Playdate development. Figured it would be “more fun” to build Playdate stuff using all Panic software, and besides, any excuse to give Panic my money is a good one. It’s a nice feeling app. Nice enough for me to give up GitHub Copilot during my day job? Probably not. But I dunno! The urge to be a total and complete Panic fanboy is pretty strong.


  • Howdy! This is my weekly post where I talk about whatever programming stuff I’ve been up to.

    MultiClock

    This week I made two small changes to the MultiClock 1.2 Beta PR to add an option to remove the dial design. I realized a day or two ago that it was kind of weird there was no “None” option for dials, though I don’t think the screensaver looks very good without a dial. But I already think some of the options don’t look good, that’s not really for me to decide anyway… to each their own!

    I also finally remembered to enable ignoreSiblingDrawOrder, which is allegedly a good optimization strategy, because, well, this person explains it as well as I could it:

    When ignoresSiblingOrder is false, SpriteKit renders nodes in the order they exist in their parent’s children array — that is, the array order determines which one draws “on top of” the other. It also means SpriteKit has to render each node one at a time, so you’re losing efficiency to OpenGL draw call overhead. 

    When ignoresSiblingOrder is true, SpriteKit relies exclusively on the zPosition property for figuring out what order to draw in. This means it can sometimes combine everything at the same z into a single draw, which makes rendering faster. But it also means that if you want to control which nodes draw in front of which others, you need to set their zPosition appropriately.

    rickster on StackOverflow

    Version 1.2 contains the very important animation system changes that fixes multi monitor support, but I still feel like it needs something more to justify its full release. I’m not sure what though. I do kinda want to redo the animation system more so that hands can move at variable speeds, and possibly even in multiple directions, but… Not feeling that pull strongly enough yet.

    There’s also the Apple TV version, kinda forgot about that idea…

    Not Much Else

    Haven’t been up to much else. I spent a bunch of time playing Lost Ark, and now Elden Ring, so I haven’t been coding again. I am starting to feel the itch though, as evidenced by me changing this website up. Building out the products page made me look for screenshots of Textile, and it really made me miss how much fun it was to play a game that I was building out, so maybe I should pick up work on rpg-laravel again.

    We’ll see! Elden Ring is pretty soul-sucking. I think I can hear it calling to me now, gotta run!


  • I migrated this blog from Gatsby to WordPress. I’m sure to many people that sounds insane. There’s so many tutorials online on how to migrate in the other direction, and even plugins that export all your content to Markdown files to stick into Gatsby, but nothing if you want to go another way.

    I’m doing it mainly for ease of use. I’m old school, I’ve been using WordPress since 2003 at least. HTML and CSS are in my blood. You know what isn’t in my blood? JavaScript, React, dealing with NPM. Eventually I gave up trying to modify my Gatsby site because getting the dev environment to run locally became headache inducing.

    WordPress, on the other hand, is very easy to reconfigure. I just update the CSS file! I started with BlankSlate, a set of template files with no styling applied. It was perfect for me. I didn’t want to have to learn what all the new template files and methodologies were. I just wanted something I could style and hack around, and this theme was perfect.

    I also wanted to be able to easily write posts on my site, without having to load up a git client on my phone. Being able to load up the WordPress app on my phone and write this post on a whim at my kitchen table is a serious perk. It’s also much easier to add photos and videos to my posts, I can upload them right in the interface. WordPress gets a lot of flack from engineers, but you really can’t beat the ease of use.

    (Not everything is perfect, the new posting interface can be a bit glitchy and annoying at times.)

    In a similar vein, I wanted to be able to toss off small, microblog style posts without titles, kinda like media rich tweets. WordPress isn’t 100% built to be happy about posts without titles, but it works, and it was extremely easy to modify the styles so that micro posts have a Twitter-like post format, and long-form posts have a more traditional blog look.

    It boggles the mind that I have been blogging in some form for 23+ years now. What is time? It seems like it wasn’t that long ago that I was an angsty freshman in high school, using Blogger. Now I’m an angsty adult, on the precipice of my 40’s, still using WordPress. The time, it flies! Either way, you should be able to expect me to write and share a wider variety of content on here now.

    We’ll see!


  • Howdy! This is my weekly post where I talk about whatever programming stuff I’ve been up to. Let’s just dive right in!

    dotBeat Internet Time

    You might have noticed that last week I released a small macOS app called dotBeat Internet Time. Let’s talk a little about the why and how I built it.

    When I started streaming a few weeks ago, I figured it would be a good idea to think of future ideas to build on stream. I even started a GitHub Project for stream ideas. Earlier in the week, my friend Dan Crum and I had been talking about the glory days of the internet, and one of us mentioned Swatch Internet Time, a throwback to 1998. I thought of this when coming up with project ideas, I thought it’d be pretty easy to create a menu bar app to show the internet time, and threw it on.

    Before setting to work however, I wanted to size up “the competition”, to see what else was out there already.

    The first app I found, BeatBar does the job, basically. It shows the internet time in the menu bar, and then features a drop-down SwiftUI interface for converting internet time to local time and vice versa. There’s a couple downsides to this app, in my opinion:

    1. It uses SwiftUI, which means you must be running macOS Catalina or newer, which I learned with PiBar is a big negative to some people.
    2. The drop down UI is confusing, with two sliders that change times in a weirdly arbitrary way (the top slider does increments of 10 .beats, the bottom slider does increments of 15 minutes).
    3. The app is priced at $4.99 in the Mac App Store. That seems like a lot of money to me.

    Next up, iBeatTime in the Mac App Store is a free app, which also displays the internet time in your menu bar. It has a ‘converter’ interface for changing your time to internet time and vice versa. Somehow the interface ends up being even worse than BeatBar, with just two fields and you have to select which conversion you want to do. This makes little to no sense to me, but at least the app is free.

    BeatTime has apps for every platform, but the macOS app is basically a glorified widget and doesn’t appear to display the time in the menu bar.

    There are also a couple iOS apps in the App Store, like Internet Beat and Swatch Beats and they all look pretty terrible. There’s an Apple Watch only app, @Watch which doesn’t look too bad.

    So all in all I came away with the impression that there’s definitely room for improvement in regards to the apps out there currently. They either have somewhat bizarre UX or just poor design and appearances in general. I figured I could definitely do better, though there isn’t exactly a lot of space for improvement: displaying the internet time in the menu bar is pretty straightforward, and the ‘converter’ function doesn’t seem that elaborate. In one app I saw (not sure if I found it again and linked to it above) the ‘converter’ functionality allowed you to set a timezone. This seemed like a good idea to me: when you receive an internet time, you obviously need to convert it to your local time; but when you are sending an internet time, it becomes more important to be aware of what the other person’s timezone may be. You don’t want to set an internet time that is going to be 3AM for the recipient.

    But what does a good UX for that look like? The one app that had this feature just had a bunch of timezones in a drop down. This works, I guess, but you have to already know what timezone the person is in, and when you have one timezone picked, you can’t see the others. So I figured… why not display them all? Additionally, I simplified the ‘selection’ interface down to a single scroll bar. This scroll bar adjusts the .beat from 0 to 999, showing you your local time the whole way. No need for multiple scroll-bars or different inputs.

    You can watch me build most of the app and ruminate over these design decisions in the VOD for the stream. The stream misses out on some of the more fun work, like programmically building out the massive timezone UI, which you can see in the final SimulateTimeViewController. You can see the code is actually pretty simple. I set up an array with all the timezones I want to display (and there are actually many more timezones than the 32 I display in dotBeat) and a fallback name in English. Then when the window loads, I iterate on that array and do some extremely lazy pattern matching to set up all the constraints for them.

    It’s really Apple’s Date, TimeZone, and DateFormatter classes doing nearly all the heavy lifting. You get a lot of good functionality out of those classes, namely localization. While I haven’t run dotBeat in any non-English locales, it should automatically adjust the times and names of the timezones to match a users locale. If you set your mac to use 24-hour time, it’ll change the times in the app to match. That’s pretty neat!

    As with my other projects, dotBeat is open source and free to download. You can check out the source and download it on GitHub. If you’re interested in supporting me as a developer, you can also purchase it for $1.99 on the Mac App Store.


  • This week on stream I built a small macOS menu bar app I’m calling dotBeat Internet Time. It’s a small, focused app with two features: 1) It displays the current Swatch Internet Time in the menu bar, and 2) It allows you to figure out how Internet Time corresponds to your local time, as well as most of the timezones on earth.

    What is Internet Time? Back in 1998, Swatch (the Swiss watch company) thought that in our rapidly globalizing world of the early internet, a globalized method of telling the time was necessary. They decided that each 24 hour period would be broken up into 1000 “.beats” instead of 1440 minutes, and that they would start counting from midnight “BMT” (GMT+1). These beats are notated with an @ symbol. So for example, @42 is 4:00 PM local time in California, but it’s 5:45 AM the next day in Nepal.

    dotBeat Internet Time screenshot

    You can download it for free from the releases page on GitHub.

    You can also buy it on the Mac App Store for $1.99, if you want to be nice and show some support for me as a developer.

    The app, like my others, is open source.


  • Howdy! This is supposed to be my weekly post where I talk about whatever programming stuff I’ve been up to. Let’s just dive right in

    Laravel & Streaming

    Like I said last week, I planned to start streaming this week and was going to use it as an opportunity to learn Laravel, so that I could make a web-based game. Well, I did it! I streamed four times (Monday, Tuesday, Wednesday, and Friday) for 3 hours each time. The only full-time viewer was my friend Dan, though a few people wordlessly filtered in and out. I don’t think it’s very exciting to watch me program, though you do get to hear about my thought process as I build things.

    The first two days of the stream were spent watching this Laracast series, Laravel 8 from Scratch so that I could learn some of the basics really thoroughly before jumping in. This was a bit of a slog, the tutorial lies in the sweet spot where an inexperienced programmer could get lost, and an experienced programmer could get bored. I got bored about 7 hours in, which is pretty good considering it was a 9 hour tutorial.

    Instead of jumping into my ultimate goal project, I decided to revisit an old project of mine called Textlike. It was a very rudimentary web-based roguelike I built out of PHP about 10 years ago. I figured it would be cool to try to rebuild it in Laravel, since it’s inner workings are still somewhat familiar to me, so I can focus on learning the ins and outs.

    By the end of the week, I got pretty far into some of the basics: floor generation is complete. Initially I got moving between rooms working, and once that was working I set my sights on handling room generation. The way Textlike generated rooms 10 years ago was entirely on-demand and didn’t make for very interesting floor layouts. This time around I decided to lean on maze generation, which I researched a bit. Then I found some existing PHP code and adapted it for my needs, using it as the data model used to create all the rooms on a floor and define the connections between them.

    So now some important things are done: account creation (very easy thanks to Laravel Breeze), character creation and deletion, and room navigation. I’ve already got enough code in place that I think it’s starting to get a bit smelly. I’ve put a lot of business logic inside my models, which I think is a code smell. I’m also not well versed in Laravel Livewire best practices, and I feel like the way I have things set up right now is a bit weird, with multiple components… I could get more into it, but maybe I’ll wait for a later post once I know more about Livewire.


    That’s actually it for this week. I’m writing this on Tuesday, so I’m late, and I’ve already done some cool things I’ll have to write about next week! Maybe there will be an announcement post tomorrow, for a project I started on stream yesterday and finished today. We’ll see!



  • Howdy! This is supposed to be my weekly post where I talk about whatever programming stuff I’ve been up to. But if you’ve been paying attention, it’s been an entire year since I last wrote a blog post. That’s because I haven’t been doing any programming for the past year, outside of my job anyway (winky face). But this week I did start doing some programming stuff again, so… here I am, writing another post about it.


    Streaming

    I’ve been thinking about streaming. After a couple trial runs playing video games, I realized I don’t think I would enjoy streaming video games, so instead I decided I would try out streaming while working on programming stuff. I haven’t streamed much so far, though I am streaming right now as I write this, but I feel like it’s kind of enjoyable, even if no one is watching outside of two of my friends.

    So, as Paymoney Wubby would say, I’m streaming right now live at Twitch.com/amiantos. I’m probably not, but I am going to try to stream every time I am doing programming stuff, so there’s no schedule at the moment, but if it’s night time or the weekend there may be a good a chance that I am online and streaming programming junk.

    Or, by the time you’ve read this, I’ve already decided it’s too much work and makes my throat dry when I have to talk continuously on stream to nobody.


    Gamebook Engine

    Over the weekend I got an email from someone that said:

    all my friends used to play games on gamebooks but now we cant find how to download it anymore.

    theres a testflight link on https://github.com/amiantos/gamebookengine but my friend cant get it to work.

    how do i download gamebook engine in 2021??

    thanks so much for making this cool app!!!!

    This is pretty cool, because I didn’t honestly think anyone ever used Gamebook Engine. But it turns out some people have, and have even developed a little habit of making gamebooks with or for their friends. That’s pretty fuckin’ neat if you ask me!

    So yesterday I loaded up Xcode and pushed out a new build so that you can now download Gamebook Engine to your iOS devices if you so choose.

    In a response, this user brought up some feature suggestions that I added to the Issues on GitHub, because I think they’re pretty decent ideas. And besides, who are you going to listen to if not the people who use your own app more than you do?

    do you think you will add a way to organise variables (like put them in folders for them or something)? it would also be cool to separate a game into parts or chapters or something so the overview isnt so complicated for big games


    Life Saver

    I got a second email over the weekend from a fan of my Life Saver screensaver and wondered if there was any chance I would release an M1 compatible version.

    I had been previously contacted by a user of PiBar asking for an M1 version and doing so was simply a matter of opening Xcode and doing an new build of the app. So I figured I could do this for Life Saver.

    Since the last release of Life Saver nearly two years ago, someone submitted a pull request to add the additional grid sizes from the Apple TV version to the screensaver. They did it in kind of a sloppy way, so I had to do a tiiiiiny bit of work to clean up the UI, thanks to a suggestion from my friend Dan while I was streaming the session. But other than that it was no work at all, and I pretty quickly stumbled my way through code-signing the new version of the screensaver and punted it off to the person who emailed me.

    However as I started writing this post, I got an email from them saying that the screensaver still does not work on their M1 mac. So that’s too bad. I’m going to have to look back into that and see if there is some special build option or something else I’m going to have to do to get M1 compatibility.


    Laravel?

    I recently started playing an idle game called Melvor Idle, which I like quite a bit. I went poking around the website version only to see that it was built somewhat similarly some of my earliest PHP-based projects, just with more javascript.

    I’ve been hankering for several months for a new project to work on, and Melvor inspired me to pick up web development again. I like iOS development, but I just want to fuck around, and I don’t want to have to learn SwiftUI and Combine and whatever other new stuff is going on in the world of iOS. I also feel like web apps can reach more people more easily, so more people will mess with my stuff than those who have to download an app and so on. And besides, if I build something awesome for the web, I am more than capable of building an iOS version, so there’s no real loss here.

    So, inspired by Melvor’s use of PHP, I decided I wanted to learn Laravel.

    Laravel looks really cool, and with stuff like Laravel Livewire you can do a lot of dynamic stuff without manually writing a bunch of unique APIs and dealing with AJAX calls everywhere. So it seems well-suited to building some sort of web game, either an idle game or some other sort of interface-based game.

    I’ve only started dipping my toes in, but it seems really cool so far, except for the fact that I have to start writing semi-colons everywhere in my code, which is pretty damn jarring as a python developer. What can you do?


    Well, I think that’s it for me this week. I’ll probably continue learning Laravel and faffing about on my stream. So, if you’re around and bored, and want to listen to my incredible voice, it’s free of charge over on Twitch. Have fun!


  • Cyberpunk 2077 is quite an achievement. Is it absolutely perfect? No. But the ways it’s not perfect can (and likely will) be fixed with patches. Will it ever live up the absurd levels of marketing hype? Probably not, but that’s okay. The hype was ridiculous, and the ways it has fallen short of it are fairly unimportant to the gameplay in my opinion.

    The main thing about Cyberpunk 2077 is how immaculately set-dressed the world is. Nearly every accessible part of the world is just a huge mess of garbage and grime, but also things of technological beauty. The amount of detail and design work put into the drivable vehicles alone is mind-boggling. Cyberpunk’s rendering of a large capitalist dystopia is akin to Red Dead Redemption 2’s rendering of the American west. It’s basically the perfect idea of these places. Is it 100% realistic? No, it’s the theme park version, but isn’t that better?

    The nicest graphics in the world wouldn’t save the game if the gameplay wasn’t fun, and Cyberpunk delivers there as well. The gunplay feels great, very punchy and visceral. The RPG progression eventually turns you into an overpowered unstoppable murder machine, but isn’t that how it should be? You start off having to hit people 5 times in the head to kill them, and by the end you’re doing somersaults and slicing people’s heads off with retractable mantis blades. Sounds about right to me.

    The main story and the sub-stories that compose the bulk of your first 40 hours in Night City are the glue that holds the whole thing together. The main storyline is fairly interesting, and some of the other storylines definitely go interesting places, but it’s mainly the characters that actually make you feel things here and there. Sometimes that might be annoyance when the characters make mistakes despite your best efforts, or admiration when you see another side of a character you didn’t expect.

    Cyberpunk is unfortunately a buggy game, with some rough edges here and there. I don’t think the bugs I’ve encountered are anything worse than you usually get with a game of this scale, but we’re in an age of the internet where every minor complaint must be amplified until it becomes a scandal that must be brought up every time the game is mentioned. That’s too bad, because it almost prevented me from getting the game. That said, maybe all the negative hype on release helped me adjust my expectations, so that I’ve been able to enjoy what is a monumental achievement, instead of sitting around nitpicking it to death.

    As a bonus, here’s some pictures I took with photo mode while I played the game. There shouldn’t be any spoilers.


  • When I started developing for PICO-8 a couple weeks ago, I quickly realized that it wasn’t really built in a way to be very portable. I like to develop on a couple different computers, so it’s important to me that I can quickly transfer files around and maintain a consistent development environment between machines. So, I quickly hacked out an easy way to do that, and along the way made my life a lot easier. I figured I would share my tips for PICO-8 development in case it helps out other people.

    First up, fair warning, I develop on macOS, so this post will be macOS-centric. I’m sure many of these tips extend to Windows, but it’s an exercise to the Windows-using reader to figure out how. I also admit that I’m an established developer who is already very familiar with most development concepts, so I may gloss over things that seem simple to me, but are impossibly difficult for a newbie. I hope not, but it might happen!

    One Folder to Rule Them All

    By default, PICO-8 stores all your carts in a folder tucked away in your ~/Library. Sure, you can type folder in the CLI to open it up, but that’s not really my style. I keep all of my programming projects in ~/Coding, and I wanted to keep my PICO-8 projects in there as well. On top of that, I didn’t want to have to put PICO-8 in my Applications directory on each machine I develop on, because I’m super lazy.

    I created a pico-8 folder in my ~/Coding folder and put my PICO-8.app in there. I also created a carts directory to store all the carts I make or download. In the end, my folder structure looks something like this:

    pico-8/
    ┣ PICO-8.app
    ┣ carts/
    ┃ ┣ bob/
    ┃ ┃ ┣ bob.p8
    ┃ ┃ ┣ comms.lua
    ┃ ┃ ┣ jrpg.lua
    ┃ ┃ ┣ overworld.lua
    ┃ ┃ ┣ shmup.lua
    ┃ ┃ ┗ transitions.lua
    ┃ ┣ demos/
    ┃ ┣ utils/
    ┃ ┣ zines/
    ┃ ┣ life.p8
    ┃ ┗ template.p8
    ┣ .gitignore
    ┣ PICO-8_CheatSheet.png
    ┣ README.md
    ┣ license.txt
    ┣ pico-8.code-workspace
    ┗ pico-8.txt

    In order to make PICO-8 realize you’ve changed your carts directory, you’ll need to venture into the main place it stores your config.txt on each computer you develop on. If you want to be fancy about it, open up your Terminal and type in nano "~/Library/Application Support/pico-8/config.txt"

    Pro-tip: If you do not see a config.txt, you need to close PICO-8. It’s only generated the first time you close PICO-8.

    Scroll down and find the header root_path and change that path to the new location of your carts folder. For me, that looks like this:

    // Location of pico-8's root folder
    root_path /Users/amiantos/Coding/pico-8/carts/

    Save the config file by hitting Ctrl+X, Shift+Y, and then Enter (if you’re editing with nano).

    Note: this config.txt file does not support the tilde shortcut, so you can not put in root_path ~/Coding/pico-8/carts/ for example.

    Re-open PICO-8 and type ls to confirm your new carts folder is there. If you got nothing in it, put something in it by typing save tempfile then type ls to see the file in the PICO-8 CLI and then confirm it appeared in your proper carts file. Did it? Great!

    Easier Debugging

    Every developer worth their salt knows that the best way to debug anything is to simply print or console.log stuff. Sure, in some environments you’ve got breakpoints and all that, but with PICO-8 we’re kicking it old school. Unfortunately the print command in PICO-8 draws to the screen, which isn’t really ideal because the screen probably has other stuff being drawn to it that we’d like to see instead.

    Luckily, PICO-8 has a printh command, which will print logs out to the command line. But you need to launch PICO-8 from the command line to benefit from this behavior, which isn’t totally straight forward. Let’s go from 0 to 60 and make it real easy, real quick. We’re going to create an CLI alias to launch PICO-8 from the Terminal whenever we want.

    Step one: Open Terminal

    Step two: Type in nano ~/.zshrc

    Step three: In this file, add this line somewhere, replacing the folder name with the path to your PICO-8.app.

    alias -g pico8="~/Coding/pico-8/PICO-8.app/Contents/MacOS/pico8"

    As above, hit Ctrl+X, then Shift+Y, then Enter to save your changes. Close your Terminal and open a new window. Type in pico8, and pico-8 will launch! Let’s test it out to make sure it’s working. His ESC to bring up the PICO-8 editor and type in printh("hello world"), then hit Cmd+R to run the file. In your Terminal window, you should see the text hello world.

    Now when you’re building your game, if you’re curious about what is happening as you play, you can put printh statements all over the place. This is an invaluable tool for all pico-8 developers, and makes it really easy to debug issues with your game loops and so on.

    Step Up Your Editor Game

    PICO-8 comes with a code editor built in, and while it’s fun to use when you’re learning or building small projects, it becomes kind of hard to stay organized. It’s a lot nicer to use a code editor like VS Code to program, but you can run into conflicts if you edit your .p8 files in VS Code while you’re also drawing sprites or making music within PICO-8. On top of that, editing .p8 files directly in VS Code is just kind of cumbersome and while there are PICO-8 extensions for VS Code, they’re not absolutely ideal.

    Luckily, you can use #import statements to make life a little easier. Look back up at that directory structure posted above. Do you see that bob/ folder? That’s one of my projects. The bob.p8 file is the core project file, but all the code for the project is actually stored in .lua files. This lets you split your code up into multiple files, so you can separate your files by game mode or more. When you run or export your project, set up correctly, PICO-8 knows to automatically import all the code from those files. It’s seamless!

    It’s also very easy. Just create a .lua file in the same directory as your .p8 file, let’s call it test.lua. Put some code in it, maybe printh("hello world") from above? Now, go into the code editor in PICO-8 and type in #include test.lua and run your project. If everything went according to plan, you should see hello world in your Terminal. Congrats!

    You can #include as many .lua files as you need for your project. The sky is the limit! So far I like to keep each of my ‘game modes’ split up into separate files, just to stay organized. Now you can safely edit your code in VS Code, while editing sprites and music in PICO-8, and never worry about losing code or graphics. Just be sure to keep your code all in these Lua files!

    Gotta Git Up to Get Down

    If you’re a newbie developer, you probably have no idea what Git is. That’s okay. But you should know, and you will know if you’re going to make a career out of development.

    I’m not going to go deep into how git works or how to use it. There’s plenty of tutorials out there, like this one, for beginners. The main thing you should know is this: Git makes it very easy for you to backup your code, and when you start collaborating with other people, it makes it extremely easy to collaborate in a safe way where you’re never in danger of losing precious code. If you’ve ever made changes to a program that you regret and cannot recover from, smart usage of Git could have saved you; if you’ve ever accidentally deleted a file or had storage die on you, git would have saved you.

    How does this apply to this guide? Well, my entire pico-8 folder I showed you above is also stored within a private GitHub repo. This means whenever I want to develop on a new computer, I just have to pull down the git repository from GitHub, and all of my files as well as my PICO-8 executable are there waiting for me. If I make changes to one of my projects, I push it to the repo, and then I can pull those changes down to my other computers. No need to worry about file transfers, or syncing folders to the cloud, or any of that nonsense. I am always in control.

    Note: Notice that I said my repo is private. If you don’t want other people looking at your code, keep it private! On top of that, do not ever store your PICO-8.app in a public repo, by doing so you’d be giving PICO-8 away for free to everyone, and that’s unethical!

    If you want to make other developers look at you funny, use a GUI client for git. I really like GitUp for macOS. As with all things related to software development, there are people who think using a GUI for git is lame and weird. But, you do you! I like how it looks.

    If you don’t intend on ever collaborating with other people, or you don’t care about backing up your code files, then don’t bother with any of this!


    That’s about it! The combination of “one folder” and “importing lua files” makes PICO-8 development a lot easier to manage.


  • A couple of years ago, at another job, I hired this guy for a data entry position. He seemed like a decently smart guy who could type fairly quickly, even if maybe he was a bit of a bro. He was at the company for well over a year, I’m pretty sure, but my memory is pretty shoddy. He’d earned “Employee of the Month” twice in one year, which was something we debated about and wasn’t just a random pick!

    Then one morning, he didn’t show up to work. No call, no email, nothing. The next day, he still wasn’t there. I’m pretty sure it was this day, or the next, I called his emergency contact: his girlfriend; just to make sure he was okay! I wasn’t trying to be a nosey up-your-ass manager or anything, it was unusual for him to just no show… plus we needed to know if we needed to hire someone else…

    His girlfriend answered the phone. I told her who I was, her boyfriend’s manager, and that we hadn’t heard from him in two or three days and we’re just checking in to make sure he’s okay? She said, “Uhh, hold on a second,” and hung up the phone. When I called her back (maybe we’d gotten disconnected?) it went to voicemail.

    At this point it seemed pretty clear that he wasn’t going to come back to work, so we set about looking for his replacement. Pretty lame that he did that, but who knows why people do the things they do? A week or two later, the company got the paperwork from the unemployment office for him. He’s trying to get money from the EDD!

    The company explains to EDD: No, he no-called, no-showed, he probably shouldn’t get unemployment. And then the guy tells EDD: I was actually secretly fired by my manager on the morning of *insert date here*. But *I* was his manager! And I didn’t do that. I don’t know if he elected for it or if it just automatically happened, but a hearing in front of a judge was scheduled for a week later. Normally the company wouldn’t have gone to such great lengths to deny someone unemployment, but he just flaked out. Very uncool.

    The first absurd thing about the story is that he was a *good* employee. I had been a manager long enough to know that it’s a pain in the ass to hire people, and it sucks when they’re not good employees because it’s such a waste of time and energy. The last thing I’d want to do is willingly get rid of a good employee. And we had proof he was a good employee: emails I’d sent to others about him; emails I’d sent *to* him directly, praising him; and the aforementioned two time Employee of the Month receipts.

    I also had a bit of an ace up my sleeve: we had security cameras everywhere. He knew this, because he used them sometimes as part of his training. So, trying to be well prepared for the ‘hearing’ or whatever it would be, I sat down and I exhaustively recorded my whereabouts on the day in question. I noted down every time I left my desk, where I went, how long I was gone, and who was in the rooms with me where I was. I was almost never alone, and always on camera. I’m pretty sure I also prepared a statement, in addition to the email ‘evidence packet’, just to be thorough.

    Unfortunately, the truth about this guy is that while he was a good employee, he wasn’t a happy one. He had bigger aspirations. When getting to know him, I’d asked him what he most wanted to do, for work, with his life, or whatever. He said he wanted to be an entrepreneur. An entrepreneur of what? I asked. He didn’t have an answer, it didn’t matter, he just wanted to be *an entrepreneur*.

    He wasn’t happy that he was a ‘lowly’ data entry person. We’d tried to get him involved in the inventory security program we had, but looking at camera footage bored him. He asked at one point if the company would pay him while he learned how to grow marijuana, so he could become a sales person and make more money. The company said no, we won’t pay you to learn to grow, but we’ll give you access to our training program documents so you can learn on your own. Disappointed, he declined the offer. Unfortunately for him, there just weren’t any open positions in the company he’d be a good fit for, so he was stuck doing data entry.

    This is mostly just making fun of him, but I’ll allow it: one day a nearby company put out a pallet of 3M foam earplugs, boxes within boxes, a couple hundred earplugs a piece. We all took a couple boxes. He came to work the next day saying he was going to sell the earplugs online, with photos of Hilary Clinton and Donald Trump glued to them, as a novelty gift, with statements like, “the holidays might get loud this year!” or something. This idea never actually got off the ground.

    The day of the hearing, I arrived with the HR assistant and we sat at a long table. My former data-entry clerk was sitting directly across the table from us. Some judge sat down at the end of the table taking notes. I honestly don’t remember who spoke first, but the story was pretty simple: He alleged that, sometime on the morning in question, I walked upstairs to where he was working, walked him down through the building, through the back offices, out to the back of building by the street, and told him we’d no longer be needing his services, and he immediately walked down to the bus stop and left.

    So I asked him, what time did this happen? And I don’t remember what time he said, but it didn’t matter, I responded: Well, I have this chart here I made of where I was, and who I was with, for every moment of the day, and at no point was he anywhere to be seen. I submitted that shit into evidence, which wasn’t as dramatic as it sounds when you say it that way, all I did was hand it to the judge. The judge asked him if he had any objection to it, and he said of course, and explained that I was in charge of security cameras–true–so I could fake the whole thing or just make up a list. The judge didn’t find that very convincing.

    I also shared the fact that we knew he wasn’t happy, but that otherwise he was a good employee as evidenced by the internal emails, emails to him, and the Employee of the Month awards. He said that we gave out Employee of the Month awards just to quiet down unhappy employees, which is only true sometimes, and may have been why he received it twice in one year without us noticing…

    I also asked him some of the more obvious questions: who else witnessed me walk through the building with you? Why didn’t you go back upstairs to grab your huge bag of pistachios and other snacks, or to say bye to any of your co-workers? And when I called two days later, and your girlfriend answered, why didn’t you talk to me or call me back?

    The judge really jumped on that last one, saying: yeah, if this guy fired you, and then called your girlfriend ‘pretending’ you no-showed, wouldn’t you wanna cuss him out or something? Give him a piece of your mind? He didn’t have great answers to any of these questions, beyond that he was upset and confused about what happened, so he didn’t want to talk to me or anyone.

    At the end of the hearing the judge said, I’m pretty sure this is verbatim, “This is a peculiar situation. Two people who have entirely, completely different stories. But you both seem credible. He seems credible, and he seems credible. Very interesting.”

    Obviously, we received word the judge ruled in our favor a short time later, and rejected his claim for unemployment. He then appealed, this time with a new story. If I remember correctly, he suggested that one of his former managers (before me) didn’t like him, and that they were using me to fire him because of it. None of that came up in the hearing, though, and after review of the case by another judge his appeal was rejected.

    I don’t manage people anymore, and I’m kind of very glad that I don’t. I might have had a worse than average experience, because the company wanted to pay people the bare minimum amount needed to hire someone, *anyone*, and I ended up with employees who were often lazy, dishonest, and manipulative. You’d think being dishonest would take so much effort it’d be better to just do the job you’re meant to do, but no.

    Prior to this guy, I was used to employees telling bald-faced lies directly to my face, usually ones I couldn’t quite prove. But this was the first time that those lies directly involved me, saying I went and did things that I definitely didn’t. Luckily, I was sitting in front of an impartial judge, and at absolutely no personal risk whatsoever, so if I failed to prove that fact it wasn’t that big of a deal.

    But, in different circumstances (like if I worked for a larger company), he could have written a Medium post, or talked to a reporter, and voiced his fabricated mistreatment to the world at large. I could just imagine how the article would account, in detail, his alleged mistreatment (“they wouldn’t pay me to learn…”) and the day I led him outside and fired him, maliciously, leaving him destitute and dependent on his girlfriend.

    And then there’d be a single sentence at the end of the article: “The company denies these allegations.” That’d be all the defense I’d get, that the company would be legally allowed to give itself. I mean, I probably wouldn’t be named, but I and anyone else working there would know he was talking about me, which could hurt me. Luckily, I’m awesome, and that wouldn’t hurt my feelings too much. But there’s other people out there, less awesome, and more susceptible to hurt feelings.

    The point is: when it comes down to it, sometimes people can be dishonest about work. That can be work they did in the past for other people, or work they’re doing right now, for you. People who seem to have a relatively normal level of dysfunctional habits can suddenly, one day, just do something completely unexpected when there’s money on the line. It’s important to be aware of this, and not just blindly trust everyone who cries mistreatment at the hands of a company. *Sometimes*, not always, they’re just a grifter, looking for sympathy and a handout.


  • Last week I bought a Raspberry Pi Zero W so that I could set up a Pi-hole on my home network. If you don’t know what Pi-hole is: it’s a network-level ad and tracking script blocker. It’s different from using something like AdBlock or the Brave browser, because it works across all devices on your network automatically. Scripts and ads are blocked before the request can even be made, so in some cases it can really speed up your web browsing.

    After getting it going and seeing the nifty admin panel that shows you query stats, I realized I would really like to see those query stats very easily on my Mac. Long story short, I ended up building the app I envisioned, and even added some extra features I hadn’t planned on originally, like multiple Pi-hole support and the ability to toggle your Pi-hole(s) on and off as you please.

    PiBar is free open source software (FOSS) like everything else I’ve built, so you can grab it over on the PiBar GitHub repo. It’s also available on the App Store, if you want to show your support (and get automatic updates): Purchase PiBar on the App Store.

    I’ll be writing a WIUT post tomorrow about my development process for PiBar over the past week, so you can look forward to that, if you need something to look forward to. (If my post tomorrow is all you have to look forward to, reach out to me and we can get you the help you need. You’re not alone, we can get through this together.)


  • Recently we decided over at Lingo that we want to allow our customers to download multiple asset files at once. We store all assets in AWS S3, which doesn’t offer a method of retrieving multiple files at the same time in a single zip file. (Why not!?)

    I did some Googling around and found a Python solution that purported to work in the most ideal way: no memory usage, and no disk usage. How can this be? It sounds like magic. Basically the idea is that you *stream* the files from S3, directly into a zip file which is streaming to S3 as you add the files to it. (Still sounds like black magic.)

    Long story short, this did *not* work in Python. I don’t know why, as I followed a couple examples from the internet. Whoever suggested that it did work must not have tested it thoroughly, or was delusional, because it definitely used memory as it was building the zip file. With sufficiently large files, or a large quantity of small files, the whole thing crashed.

    Distraught that my precious Python was failing me, I found several sources on the internet that suggested this idea *did* work when using Node, and worked especially well as an AWS Lambda function. This sounded like a good idea to me, because a Lambda function will definitely not allow you to use too much memory, disk space, or time. Only problem was that I knew little to nothing about Node/JavaScript development. I guess it was time to learn!

    So… I learned. Long story short (again), it took a little bit of research, but I came up with this Lambda function which successfully zips up hundreds of files directly into a zip file on S3, without any real memory usage and definitely no disk usage. It took some research, because none of the examples people had posted online actually worked flawlessly from beginning to end. For all I know, this script has some issues, but I haven’t run into them yet, and when I do, I will update it to avoid them.

    Compared to Python, Javascript syntax is a horrific mess that makes my eyes bleed. But I can’t argue with results, this worked out while the Python solution did not.

    If you need an explanation of what this code does, just give yourself a year or two as a professional developer and you’ll realize understanding what it does isn’t particularly important, but by that time you’ll be able to figure it out.

    Good luck!


  • After three years of Numu Tracker being a part of my life (the first year of it spent slowly developing it), I am moving on and shutting it down as of today. There’s several reasons for it, so I figure I will briefly talk about them as honestly as I can, and also reflect on what I learned along the way.

    Why shut it down?

    As Numu got more popular, I ended up getting forced into a fairly expensive VPS package by Namecheap. Originally it cost about $10/mo to host, and then it skyrocketed up to $60/mo. This is mostly because I wasn’t aware of CDNs (like S3 or DigitalOcean’s Spaces) when I was building Numu and was hosting all the artist and cover art on my server directly. When I needed more space, Namecheap balked at the amount of files I had, or perhaps at my bandwidth/cpu usage, and pushed me into a VPS package.

    At this point I had my first real programming job and had begun working on a Python-based API. I liked it a lot, so I decided to get around this crazy hosting cost for Numu by rewriting Numu Tracker from scratch, to fix some of the issues it had as well as get on some different hosting. I even reached out to some people to see if I could get free hosting, since Numu was open source, and DigitalOcean agreed to host it for free and immediately gave me some credit to use for it. Things were looking up!

    However, I procrastinated on building the new version. When I eventually worked on it again, I got the new API into a semi-working state but eventually turned away to work on other things. I was just having a lot of trouble summoning enthusiasm for a project that was originally “hacked together” and was now turning into something I wanted to make more “professional”. Over the course of a year and a half I would come back to Numu 2.0, put a little work into it, get bored or overwhelmed, and stop. I ended up working on other stuff instead (like Life Saver, Aeon Garden, and Gamebook Engine). My desire to build something truly impressive was outmatched by my desire to work on other, momentarily more interesting things, that I could sloppily wander through.

    The final straw came about a week or two ago when I received some bad financial news. I decided to let it inspire me to finally finish Numu 2.0 and I sat down to start to work on it with clear eyes and a full heart, but quickly lost steam. The new version was shaping up to be not _quite_ as good as the current version, and it started to look like a lot of work, and not fun work, but hard work. I started to ask myself: I don’t make any money on this, and I don’t really want to ask anybody for money for this, and I probably shouldn’t be spending my time working on something that isn’t going to make me any money, so should I be doing this?

    The answer was, obviously, no. I felt pretty bad about it and was morose for a day or two. It’s depressing to kill off something you’ve built, that you are (or were) proud of, that you know other people like a lot. I felt a bit like I was letting down all the serious music fans like myself who regularly used Numu, especially because there had been points I “promised” that Numu wouldn’t shut down. That’s kind of a big downside to program development as your “artistic hobby” in contrast to other mediums: paintings, recorded music, the written word, film or video; once published and given to someone, these things essentially last forever. You can’t “take it away”, typically, from someone who has it and loves it, and its continued existence costs you nothing personally.

    The regret I felt about shutting it down was made worse by the fact that I had numerous opportunities to make different choices, to try to migrate v1.0 to a new host instead of insisting that I’d build v2.0 instead, or to just actually put my head down and finish it. Instead I let it stagnate and burn money. This regret was further compounded by the flood of supportive words and thanks that I received on the subreddit when I announced that Numu would be shutting down. One person reached out to me privately and offered to pay for 3 years of Numu hosting, assuming he could recoup his costs later on down the road. (After I told him how many users Numu had, and what growth was like, he agreed with me that it would likely not be worth it for him.)

    As of today, Numu is gone. The app is a useless carcass only fit for deletion. There are a couple other apps on the app store that do similar things, but it’s mostly with sadness and not pride I say that they are not nearly as good as Numu Tracker was. They’re not as well designed, they’re not as thorough, and they just don’t feel as solid and nice. Numu Tracker was one of a kind, and music fans are worse off without it.

    What did I learn?

    I learned a couple things about running an app that I think would be valuable for anyone else considering running their own app.

    Encourage people to give you money

    This is a personal failing. I like to be generous and altruistic, perhaps to a fault. I have a “hacker ethos”, according to a friend of mine, where I believe that all software (or artistic creation) should be free for all, and that money will naturally flow from that. I think this is a symptom of my privilege, and my “youthful mindset”. I’ve never been so hungry that I’ve felt the need to charge for things I build, and on top of that, my impostor syndrome leads me to believe that nothing I’ve built is worth money to other people.

    I was mistaken. Numu was worth money, at least to some people. Eventually I started to ask for money, but I did it gently and in a way most people wouldn’t notice, so it did me no good. It would have been better for me to build some sort of donation option into the app using in-app purchases. Instead I tried linking people to my blog in the hope they’d then go to my Patreon, and feel generous.

    This is too much to expect of people. At a sufficient scale, it can work, but you need millions of fans or users. You have to expect that only 0.5% of your users are enthusiastic enough to go out of their way to figure out how to give you money. If you’ve got millions of fans, this is fine because that’s still thousands of people. But when you only have thousand users, that’s maybe five people if you’re *lucky*.

    When I first published Numu, I did have IAP that were needed to unlock push notifications. But nearly no one downloaded the app, and no one paid for the notifications. I should have stood my ground, but instead I ended up making the notifications free and open sourcing the app for some publicity. Eventually, RecordBird shut down, and a flood of users came into Numu, and it would have been valuable to have an IAP around at that time because RecordBird users really wanted to find a service that wasn’t going to shut down, so I bet they would have been happy to pay for it. But they had no option to.

    In short, you should charge money for your app if it costs you any money to run. It’s great to think that you’ll always be financially capable and willing to foot the bill for all your users, but that can get out of hand especially if your app grows considerably. Even if the option is just voluntary donations, it’s better than nothing, and if you get into a tight spot you might find that your users are compassionate and generous, willing to donate when they hear you really need the financial support to keep the app alive.

    Other things I learned…

    The main other thing I learned is that users are not very vocal. If your app stops working or has bugs, they will stay quiet about it for a while. At one point I’d completely broken the app for several days, and only one person (out of a thousand or so) reached out to me to ask what was going on. You can’t rely on your users to notify you about problems in your app. Some will, but most won’t, so you need to be proactive in trying to find and fix these bugs on your own.

    App development is hard work, especially on iOS. iOS is growing more complicated as time goes on. As of April 2020, apps must have an “iPad version”, which Numu never had (though it came close to having a very ugly one last year). To make a “first class” iOS app, you have to worry about other stuff like accessibility concerns, state restoration (another coming requirement), and some other stuff I can’t think of now.

    It can also be unrewarding: I had to sit by and watch worse apps get “Featured” in the App Store, on Product Hunt, or show up on Apple blogs, because they somehow knew the right connections to make or had other advantages. I think Apple never “Featured” Numu on the App Store because it promoted services other than Apple Music (similar apps that *only* work with Apple Music have been featured, and they were much worse than Numu).

    What did I do right? I built an app I wanted for myself, and I built it so that it would do the job I needed it to do. I had faith that there were other users out there like me, who would want the same kind of app that I wanted, and that faith bore out. It was very gratifying to receive feedback from users who appreciated the “to-do list” aspect of app, considering I had several friends tell me that they didn’t understand who that was for. It was for me! Luckily, it was for some other people out there too who took music listening as seriously as I did.

    In short: If you build an app, build something you want and need. It will inspire you to struggle through the hard times, and it’ll feel extremely satisfying when you learn that you’re not alone in the world, other people have the same needs.

    What’s next?

    So what’s next for me? I don’t know exactly just yet. I think I am going to try to focus on a bigger project that I can charge money for right out the gate, some sort of game. I’ve got a solid idea, but I’m not ready to talk about it just yet.

    Will Numu ever return? I haven’t ruled it out. If I find success with some other projects, and I continue to miss Numu, maybe I will raise it from the dead as a subscription service (so I do not repeat the same mistakes). I think it’ll be a year or several years, and I hope in the meantime Apple Music or Spotify can pull their heads out of their asses and provide their users with new release lists that render an app like Numu unnecessary. I guess we’ll see.


  • I recently started working more with AWS Lambda functions, some of them with external dependencies, and I quickly ran into an issue where dependencies that are built on MacOS sometimes don’t play nicely with Amazon Linux on Lambda.

    The solution, back in the early days of Lambda, was that you would have to spin up an EC2 box, and then build and zip up your dependencies there. Luckily, things are much easier now because Amazon offers a Docker image for Amazon Linux that you can use to build your dependencies.

    I found a blog post by Quilt that discusses automating the process of creating your Lambda deployment package, but their solution stops at building the zip file you need. I wanted to take it a bit further and get the entire deployment process down to just one command, so I made some changes and additions.

    First up, you have your Dockerfile. This is basically identical to Quilt’s, except that I’ve added the awscli package to be installed.

    FROM amazonlinux:2017.03
    RUN yum -y install git \
        python36 \
        python36-pip \
        zip \
        && yum clean all
    RUN python3 -m pip install --upgrade pip \
        && python3 -m pip install boto3 awscli

    Then you need your package.sh file, this is the script that’ll get run inside your Docker container once it’s built. Note the $LAMBDA_FUNC environment variable. We’ll be setting that when we run the container via the Makefile.

    #!/bin/bash
    
    mkdir tmp666
    [ -f /io/requirements.txt ] && python3 -m pip install -r /io/requirements.txt -t tmp666
    rm -f /io/lambda.zip
    cp -r /io/* tmp666
    cd tmp666
    zip -r /io/lambda.zip *
    
    cd /io
    aws lambda update-function-code --function-name $LAMBDA_FUNC --zip-file fileb://lambda.zip
    
    rm -f /io/lambda.zip

    Before we tie it all together with the Makefile, I should clarify that I wanted this automation to be set up so that I could use it to deploy multiple different Lamdba functions to different places, so the directory structure is pretty important. In my setup, the directory structure looks like this:

    lambda
    ├── hash-function
    │   └── lambda_function.py
    ├── preview-function
    │   ├── lambda_function.py
    │   └── requirements.txt
    ├── Dockerfile
    ├── Makefile
    └── package.sh

    You’ll notice that a function may or may not have dependencies. Even without dependencies, this automation can still make developing in VSCode and deploying to Lambda slightly easier and faster (than copying, pasting, and saving). The main trick to this is that my folder names exactly match the name of the function in Lambda. So hash-function will deploy to hash-function in Lambda, and so on.

    So our final step is to create our Makefile.

    deploy-lambda:
    ifdef p
    	cp -f package.sh $(shell pwd)/$(p)/package.sh
    	docker build -t lambda .
    	docker run --rm -e LAMBDA_FUNC=$(p) -e AWS_DEFAULT_REGION=us-east-1 -e AWS_ACCESS_KEY_ID=$(shell aws --profile default configure get aws_access_key_id) -e AWS_SECRET_ACCESS_KEY=$(shell aws --profile default configure get aws_secret_access_key) -v $(shell pwd)/$(p):/io -t lambda bash /io/package.sh
    	rm -f $(shell pwd)/$(p)/package.sh
    endif

    Note that we’re automatically grabbing the necessary AWS access keys from our local machine and injecting them into the docker container, so that it can deploy the package to Lambda for us.

    Then to deploy to lambda it’s as simple as writing…

    $ make deploy-lambda p=preview-function

    That’s about it! Doing this has saved me a lot of time that would have otherwise been spent running a couple different commands to deploy my Lambda functions. Now it’s fast, easy, and worry-free. Again, kudos to Quilt and their blog post for doing more than half the work for me 😉


  • When I finished Life Saver it seemed obvious to host the downloads on S3, like the rest of the site. But I wanted to be able to track how many times it had been downloaded, and S3 doens’t have this functionality built in. Coming from a traditional web background (Apache, cPanel, etc) I wasn’t sure where to start, but luckily I saw a StackOverflow answer that pointed me toward S3stat.

    What’s really great about S3stat is that it has a nice macOS utility to help setup all the complicated AWS stuff for you. One S3stat up and running on your bucket, you get a nice looking dashboard showing you tons of info. As an example, here’s the current daily stats for my bucket. (Note that I really only host two files there.)

    My S3 bucket download stats from S3stat

    You can drill down into individual files to see stats for that specific file:

    Single-file download stats from S3stat

    One feature I like a lot is that it also shows you referrers, so I can see how people are getting to my file just in case any blogs or websites directly link to it (which hasn’t happened yet). Between this, GitHub’s traffic stats, and Google Analytics, I have a pretty complete view of where my traffic is coming from and how effective my site is.

    Referral stats from S3stat

    The S3stat service is normally $10/mo, and as an open source developer with no commercial products under my belt, I can’t really bring myself to shell out even that amount of money for it. Luckily, the owner of S3stat is a generous soul and is willing to extend my free trial into perpetuity (or until I am rolling in open source riches and can pay for it myself). Many thanks to S3stat for giving me a way to gain some insight into how frequently my files are downloaded!

    Check it out at https://www.s3stat.com!