Header image

Steven’s blog

This is where I post about my side projects so I can feel like I actually shipped something.


One day maybe I’d like to make a game. Currently focused on learning Mandarin, and building an app, Bookchoy to help with that.


Engineer at Solo.io, but I don’t write about much networking/infra/work stuff on this blog. Opinions are my own, and do not reflect those of my employer.

福州话字典:美全八音

第一次跟我老婆去马来西亚的时候,我一直要等她 帮我翻译。除了她的公公,外公,外婆之外,大家都说英语。 就是有时比较方便讲中文。回来美国我决定学习华语了。现在 我觉得我的中文不错,已经到中级了。前次回去马来西亚, 虽然他们讲中文大我概都明白,但是有时候他们还讲另一个 语言:福州话。 在网上我也找了一个会福州话的老师。上了几次课了, 我很快发现她的福州话不一样!我老婆告诉我,他们的 福州话和中国的,甚至马来西亚其他地方的,都不一样。 他们可能会用不一样的声调,不一样的词汇,等等。那个 中国福州的老师教我他们的福州话已经被普通话影响了。 上次回去古晋,她外公外婆给我一本书:美全八音。这是一本 福州话的字典。我根本不知道怎么用。首先,它用的是繁体字。 其次,它写的语法很奇怪。好像不是普通话来的,或者是古代的语法。 我仔细地用人工智能和OCR来翻译。原来,这就是一本“韵书”。 前面有一个曲子,可是书法很难看懂。 找到了一个人翻译了这个曲子! https://zhuanlan.zhihu.com/p/96448334 然后说明一下怎么用。 三十六字母 用36个字来表达福州话的语音。有一个小曲子来记住: 春花香,秋山開,嘉賓歡歌須金杯, 孤燈光輝燒銀缸, 之東郊,過西橋,雞聲催初天, 奇梅歪遮溝。 接下来,他们解释那36个字有15个字头(initial sound): 每字母分出十五字頭 # Initial Roman letter (per book) Exemplar (per book) 1 柳 l 巃 2 邊 — 枋 (???) 3 求 g 公 4 氣 k 空 5 低 d 東 6 波 p 蜂 7 他 — 通 8 曾 — 宗 9 日 — 齈 (???) 10 時 — 嵩 11 鶯 — 溫 12 蒙 — 儍/傻 (???) 13 語 — ngǔng (romanized, rising tone) 14 出 — 春 15 喜 — 風 然后,有7个声调: ...

June 13, 2026 · Steven Landow

Local-first, three sync engines later

I want to write about the hard part of this though: the sync engine. I want online persistence for two reasons with Bookchoy: I want everything I do to be backed up and accessible on any device. This is pretty much a standard expectation for any modern app. The chrome extension is an immediate second device, which means (I hope) every user will actually have utility for online persistence. Version one: two columns When I first started developing, there was only a local SQLite. No auth, no remote database, no sync engine. I knew I’d eventually build one, but I didn’t want to overengineer it before I understood what I actually needed. The whole mental model was: ...

June 10, 2026 · Steven Landow

Improved Chinese dictionary mapping model

The core piece of functionality in Bookchoy is “tap on a character, see a popup with the dictionary definition”. Sounds simple, but because of the ambiguity in the language it’s not a trivial problem. I think while it can be a crutch, having the ability to not lose your flow in reading/navigating/writing is critical, to avoid just getting bored or frustrated and stopping the session. I want this functionality everywhere: on my phone, in my browser, in my primary text editor (neovim). ...

June 7, 2026 · Steven Landow

building random small things

building random small things At this point we all know that AI lets us build whatever we want when we want it. It’s especially nice when I have small things I want for myseelf and I wouldn’t have otherwise spent the time bootstrapping and polishing them to the point that they’re daily-drivable. The reason I frequently feel the need for such tiny apps is that most things available at the top of whatever SEO optimized search are pretty bloated. For example, a fitness/macro tracker: ...

May 31, 2026 · Steven Landow

debugging Go deadlocks

UPDATE: I made this into a small Go module: https://github.com/stevenctl/deadlog. It is a drop in replacement for sync.Mutex and sync.RWMutex, with the option to call the returned unlock closure if you want to track unreleased locks. Don’t run with this in production, it’s only for debugging deadlocks. Multiple times, I’ve had to debug deadlocks in some Go code. Even though Go’s -race flag when running tests can detect deadlocks, in my experience it hasn’t caught the ones that drive me crazy with flaky or fast tests (~500ms-2s). ...

January 13, 2026 · Steven Landow

I'm gonna release my Chinese app

It’s been a while! My last post was about this app, about a year ago after a couple months of work. I did not expect this to be a long-term project. Studying Mandarin became one of my main hobbies. Using my own app to study was cool, but it had lots of issues so I kept chipping away at it over nights and weekends. At this point I use it daily. After showing my in-laws (who are Malaysian Chinese, and native speakers), they showed enormous support and ended up motivating me to polish it for release as a proper app. ...

December 1, 2025 · Steven Landow

Building my own app for learning Chinese

I’ve been studying Mandarin Chinese for a few years, starting with basic phrases (Pimsleur) and then moving on to Hello Chinese. This app was excellent for the basics, offering (quality!) gamified lessons and a content of library with a slick reading interface where you could tap unknown words for translation and audio. After completing all the structured lessons, I focused on reading. When I saved new words, they went into a separate review system: flashcards. I found this system, particularly the self-grading aspect, less engaging than the original lessons and their multiple-choice based review system. ...

July 13, 2025 · Steven Landow

3D Autotiling pt. 1: Basic WFC

Introduction I don’t think I would have become interested in shooters if it weren’t for Halo 3’s forge mode. I probably logged 1000 hours in Minecraft when I was a kid (and learned Java because of it!). Games that let you make stuff are the best. Making games is even better. After a few years focusing on my career in network programming I decided to revisit games, this time looking at 3D. ...

September 13, 2023 · Steven Landow

3D Autotiling pt. 2: Expanding and Optimizing

In the last post we were able to randomly generate “correct” looking 3D shapes. However, my goal was something that a user or game developer could create levels with. Let’s look at our requirements: The user should have direct control of the generation. BorisTheBrave describes calls this “Driven WFC”. We need a lot of tiles. That would take a lot of time to model for a game developer. This isn’t totally avoidable, but we can optimize our workflow. If the user is going to interact with this, the algorithm for solving the generation needs to be fast. Our naive implementation needs a major upgrade. Driving WFC Initial Constraint Our method of “driving” WFC will be specifying which cells aren’t empty as an initial contraint. ...

September 13, 2023 · Steven Landow

Faking Buoyancy

During the intro to a game I (perodically) work on, the player starts on a pirate ship. Realistic water doesn’t fit the style, and scroling textures on a big plane looked pretty boring. To make things a bit angrier I needed intense waves, and I needed those waved to actually affect the world. Waves To keep it simple, I just did procedural heightmap; i.e. set the vertical coorinate to be a function of the horizontal position and time. ...

July 4, 2023 · Steven Landow