Following my CRDT explorations earlier this year, I wanted to build an app on top of a persistence layer that adopted the same kinds of resilient sync patterns, even if it didn’t use the actual low-level “ORDT” data structures described in the article. Having used a spreadsheet to track my drinking over the past few years, a dedicated drink tracking app seemed like the perfect little project.
The main problem I was aiming to solve was architectural. Many apps establish rather unclear relationships between the main app, the persistent store, and the cloud. Does the local database or the cloud count as the primary store? When a change comes down from the UI layer, how does it eventually get synced to the local database and to other devices, and what code keeps track of all this state? Which system does the cloud talk to? How is offline mode dealt with? How do critical errors percolate through this pipework? And what does the in-memory cache have to say about all of this? A common result is that the data layer becomes a monstrous thing that deals with persistence, sync, caching, reachability changes, UI notifications, and many other systems, all at once.
The reason for this mess, in my mind, is state. Sync often relies on systems catching every change and remembering their exact place in the whole process. If an update falls through the cracks, the data has a high chance of desyncing. Naturally, this leads to monoliths that try to claim ownership over every last bit of data.
Ergo, my goal was to get rid of as much state as possible.Continue Reading...
It’s been a year and a half since I wrote my article on building a hassle-free Thunderbolt 2 eGPU for my late-2013 15” Macbook Pro. In all this time—with the caveat that I was exclusively using an external monitor through Bootcamp—I’ve had essentially zero issues with the setup. Booting would occasionally require one or two attempts to hear the Mac chime, but once you were in, performance was flawless. The GTX 1050 Ti was quite an excellent little card.
However, my power requirements recently changed. When the Oculus Rift went on sale for $350, I just couldn’t resist picking one up. VR was new to me and I wasn’t sure if this was something that would stick, but my first experience with Beat Saber and The Climb left me in awe. This was, very clearly, a new category of human experience that was being offered for just a few hundred bucks! Unfortunately, latecy was now an issue with some of the higher-caliber games such as Batman: Arkham VR, and 60–90fps was essentially a hard requirement for VR immersion. Together with the fact that many newer titles were starting to see poor performance even at lower settings and resolutions, I decided to explore upgrade options.
Until recently, I had my eye on the single-fan EVGA GTX 1060 SC to eventually replace my 1050 Ti, since the dimensions were very similar. But in doing this round of research, I was surprised to discover that Gigabyte offered an actual, full-fledged GTX 1080 in almost the same form factor! Though slightly too tall to fit, the card was just the right length for my AKiTiO case, meaning that I could simply take the top off and pop it in without having to do any metal-bending.Continue Reading...
The challenge: fit a rotating art gallery somewhere into my life.
I love visual art and find it hugely inspiring. Unfortunately, reading art books is too much of a context switch to be a regular distraction, while museums are only appropriate for the rare trip. Instagram helps, but it only lets you see content from artists you follow. There’s still the 99% of art history beyond that sliver!
Sourcing art wasn’t the problem. For years, I had been keeping a fairly large folder of inspiring images from places such as Imgur albums, RuTracker museum collections, /r/ImaginaryNetwork, and /r/museum. But leafing through them wasn’t enough: I needed to put them into a regular and random rotation in a place that was just out of eyeshot, but without becoming an overt distraction.
In 2015, I finally solved the problem by building an app called Backgroundifier, which converted arbitrary-size images into wallpapers by superimposing them onto attractive, blurred backgrounds. By pairing an Automator Folder Action with the native wallpaper cycling functionality of macOS, I could now drop arbitrary images into a directory on my desktop and have them automatically show up in my wallpaper rotation. Peeking at an image was as simple as invoking the Show Desktop shortcut, and if I wanted to see something new, all I had to do was switch to a new Space.
For several years, this scheme worked perfectly fine. But recently, my collection had grown to over 500 images, and I found myself bumping into some slight annoyances. For example, I had no way to retrieve the filename of the current wallpaper, to remove an image from rotation, or to mark it as a favorite. Every maintenance task had to be performed manually.
Finally, I decided to build a menu bar app that would solve all my problems through a unified interface: BackgroundifierBuddy.Continue Reading...
(Sorry about the length! At some point in the distant past, this was supposed to be a short blog post. If you like, you can skip straight to the demo section to get a sense of what this article is about.)
Embarrassingly, most of my app development to date has been confined to local devices. Programmers like to gloat about the stupendous mental castles they build of their circuitous, multi-level architectures, but not me. In truth, networks leave me quite perplexed. I start thinking about data serializing to bits, servers performing their arcane handshakes and voting rituals, merge conflicts pushing into app-space and starting the whole process over again—and it all just turns to mush in my head. For peace of mind, my code needs to be locally provable, and this means things like idempotent functions, decoupled modules, contiguous data structures, immutable objects. Networks, unfortunately, throw a giant wrench in the works.
Sometime last year, after realizing that most of my document-based apps would probably need to support sync and collaboration in the future, I decided to finally take a stab at the problem. Granted, there were tons of frameworks that promised to do the hard work of data model replication for me, but I didn’t want to black-box the most important part of my code. My gut told me that there had to be some arcane bit of foundational knowledge that would allow me to sync my documents in a refined and functional way, decoupled from the stateful spaghetti of the underlying network layer. Instead of downloading a Github framework and smacking the build button, I wanted to develop a base set of skills that would allow me to easily network any document-based app in the future, even if I was starting from scratch.Continue Reading...
I am once again in the market for Bluetooth headphones. My last pair, the first edition Sony MDR-1RBT, served me very well for the last four-and-a-half years. I didn’t (and still don’t) have much experience with the high end of the audio market, but when I was picking them out, they were among the best sounding headphones I’d heard. The feeling of space was especially startling: for the first time in a headphone, I felt like the music was all around me instead of being localized to a point between my ears. Now, parts of it were held together with glue and the remaining pleather was flaking all over my jacket. The time was right for an upgrade.
I started to research comparable, $200 to $400 over-ear wireless replacements. My wishlist included noise cancellation, multi-device pairing, volume controls that interfaced with your device, tactile controls with previous/next buttons, and foldability. The only real technical requirements were audio quality, a zero-latency wired connection, and at least some kind of physical control. But non-technical, quality-of-life attributes were also very important. It’s hard to deny that headphones play many important roles in our lives apart from simply reproducing audio. They are most certainly a fashion item. They serve as earmuffs in cold weather. They block out the outside world when we need to retreat into our work. For many of us, they’ve become one of the most important and frequently used accessories in our wardrobe! Soon, we’ll be seeing health and fitness sensors incorporated right into the earcups. Especially with the cord cut, there’s a lot more to headphones than just audio these days.Continue Reading...
In the course of doing latency testing for my previous article on the Logitech MX Master, I discovered a couple of flaws in my helper app, and I also realized that I should have probably recorded a few more sample points. So now, as a followup, I have devised a better testing methodology and run a full suite of tests. Unfortunately, with this new data in hand, I must now retract my original recommendation. The Master is still a good mouse for the average user, but its wireless performance is just too unreliable for precise gaming or Bluetooth use.
If you’re looking for a great all-arounder, I would instead give my highest recommendation to the G403 Wireless, which I’ve been happily using for several months with zero issues. While this mouse does require a dongle and only has a tenth of the Master’s battery life, its best-in-class performance, non-existent latency, svelte form factor, and incredible clicky side buttons more than make up for these downsides. Better yet, you can routinely find it on sale for $50 or lower on Amazon and at Best Buy. I’ll try to post a fuller account sometime in the near future.
In the meantime, here are the new test results for the MX Master, G602, and MX 518.Continue Reading...
We all know that Bluetooth has an abundance of flaws, ranging from frustrating latency to arcane pairing rituals. By many measures, it still feels like a technology stuck in the early 90’s. And yet, once you’ve experienced the freedom of going wireless, it’s very hard to go back to the old ways. Reaching to unplug your headphones when leaving your desk, only to realize that you can simply walk away? Bliss!
For several years, I’ve been on the lookout for a Bluetooth mouse that could also be used for non-casual gaming. At minimum, the mouse needed to be on par with my trusty MX 518 at 1600 DPI and have little to no latency. Unfortunately, the vast majority of reputable Bluetooth mice maxed out at around 1000 DPI and had a reputation for being a bit laggy. The Razer Orochi was one of the few models that supported high DPI over Bluetooth, but it was a cramped little thing that felt rather unpleasant to use.Continue Reading...
Previously, I wrote about the Asobu Travel Mug as an excellent (if unintentional) travel gaiwan. Now, there’s a new leader in the not-a-gaiwan-but-almost-better-than-one category: the Klean Kanteen 8oz insulated tumbler.Continue Reading...
Last month, I released an unusual little app for iMessage. It’s called MusicMessages!, and it’s a collaborative step sequencer that lets you work on short pieces of music together with your friends. As far as I can tell, it’s the only app of its kind in the iMessage App Store. (Probably for good reason!)Continue Reading...
My late-2013 15” MacBook Pro’s discrete GPU — an NVIDIA GeForce GT 750M — was pretty good for gaming during the first year of its life. But around the time that the new generation of consoles dropped, AAA games on the PC started becoming unplayable, even at postage-stamp resolutions with the lowest possible settings. I lived on a strict diet of indie games from 2015 to 2016 — thank goodness for well-tuned titles like Overwatch and The Witness! — but the itch to try games like the new Mirror’s Edge and Deus Ex became too great. Initially, I thought it might be time to switch out my MacBook for the upcoming 2016 model, but the winter reveal wasn’t particularly tempting: CPU performance was about the same as mine and the GPU was — at best — 3 times as powerful. (Still need to see the benchmarks on that — educated guess.) Worth it for a few hundred bucks, but $2000? No way!
Building a gaming PC wasn’t an option due to my mobile lifestyle, and in any case the kind of CPU I could buy for cheap would be comically underpowered compared to the i7 4850HQ I already had in front of me. So I started looking into the scary world of external Thunderbolt GPUs, colloquially known as eGPU. Modern Thunderbolt 3 (allegedly) supports external GPUs in an official capacity, but older Thunderbolt 2 can get the job done as well, even though it’s unsanctioned by Intel. I’m usually reluctant to pursue these sorts of under-the-radar hobbyist projects, but there was enough prior art to make it worth a shot!Continue Reading...
Newer Page 1 of 5 Older