@_enzo_dev hire enzo
play explainer video
Name Description Tags
robata macOS window selector A window selector app for macOS css, react, mobx, tailwind, swift, macos, xcode
cssprinciples CSS blog A CSS blog for deep diving CSS principles css, react, mobx, bun, tailwind
tabserve A https url for localhost Use your web browser as a reverse proxy js, react, mobx, cloudflare, cloudflare-workers
bigWav Transcription and annotation Convert audio to a transcript and then annotate it in your browser js, react, mobx, google-cloud, node-js
Initial Instinct Multi player game Guess a famous persons initials in 30 seconds to score points. More points awarded for rarer name guesses. js, react, mobx, google-cloud, node-js, rust
Stay 100 AirBnB search engine Find hidden stays. js, react, css, tailwind, compute-engine, docker
Table Dog CLI Download your Stripe account to an SQL database. rust, sqlite, sql, google-cloud, compute-engine, docker, node-js
Note To Web Blogging engine Convert your Evernote notebook into a blog. js, node-js, react, google-cloud, compute-engine, sqlite, docker
SQLite MPI Database library Wraps the SQLite database in a message passing layer so that the same JavaScript API can be used on Android, iOS and any other platform with high reliability. rust, sqlite, js, react, react-native, ios, android
Simple Job Tracker Business management software Mobile app that keeps track of jobs for businesses that offer location based recurring services. E.g window cleaners. js, node-js, react, react-native, sqlite, android, ios, mysql, app-engine
Tree merge Category merge tool Creates a single normalized category system by merging many digitally different but semantically equal category systems. js, react, firebase, data-visualization
Everydiy Product search engine Search all the DIY stores in the UK instantly. Includes normalised categories. js, node-js, golang, react, google-cloud, compute-engine, elasticsearch, mysql
transform-x JS library JS functions to convert between SQLite, Excel and JSON js, node-js, cloudflare-workers, wasm
lilo CLI CLI to download GCP logs to a SQLite db js, bun, gcp
Durafetch JS library and CLI CLI to download Cloudflare Durable Objects js, node-js, cloudflare
Income Percentiles Interactive chart Compare income percentiles across countries. js, react, tailwind, svg
Bezier Curves Generate curves Extract curves from fonts to practise tracing over them. js, react, tailwind, svg
People By Initials Search index for notable people Enter name initials and get a list of matching names. js, python, angularjs, go, mysql, nginx
Lets Meet for Coffee Map based coffee shop search Prospective customers can book a date and coffee shop to meet at. js, python, app-engine, google-maps-api, bootstrap, backbone.js

Robata: put it on the grill

  • This is a macOS app written in Swift and HTML/JS/CSS (Mobx/React/Tailwind).

  • I created it because the default "Mission Control" window manager for macOS has some issues:

    • A. It is not possible to quickly jump from one space to another when there are other spaces between them.
      • The "three-finger side swipe" trackpad gesture only works well when swiping to a neighboring space, and when you know what you will find on the space.
        • Otherwise, you end up paying the cost of each animation as you swipe between each space searching for the window you are looking for.
    • B. "Expose" does not work well when you have more than 3 windows on a space.
      • This is the "three-finger swipe up" trackpad gesture.
      • The animation seems to "explode" the windows to show them all, but this is quite distracting and unpredictable. The more windows you have, the smaller each one will be in this view. The animation also takes a few seconds to complete.
      • The fact that windows are semi-randomly placed and small makes it difficult to visually scan them.
    • C. The top "space viewer" is so small you can barely tell which windows are on those spaces.
      • It also requires that you jump through two animations to get there "three-finger swipe up", and then mouse over the spaces row at the top of a monitor.
    • D. Stage Manager does not seem to work well with spaces, adding another layer of groupings and UI when a better interface to the existing spaces could work well.
    • E. The default cmd-tab of macOS switches between apps and not windows. It also orders by most recent, but often the window I want to switch to is around 4-6th place depending on the tasks I am currently working on.
  • The default macOS animations for spaces are nice, but when they take a few seconds and are unpredictable, I think they have a negative effect on the user experience. A good principle for animations seems to be that if they are predictable, they are good, but if the user has no idea what the animation will reveal, it makes for a worse experience as the user has to wait for the animation, then spend a lot of visual processing time working out what information the animation is presenting. You cannot disable the animations for spaces on macOS.

  • Robata has minimal animations and shows windows instantly to avoid the double-edged sword of animations. Humans have very fast visual pattern matching, which is why I want to show the actual windows, as the patterns of the window UIs for different apps should be instantly recognizable.

  • I also do not like the icon-centric design of the default macOS cmd-tab. I feel scanning for the right icon is a waste of time, as you then have to scan for the right window contents to select the window you are looking for. It is better just to remove the icons and keep the window screenshot and title, as these are more specific to the task you are actually working on in those windows. An example is Chrome—you might have 10 tabs open, so the icon does not give you any clue as to which tab you want.

  • The app uses private macOS APIs to get data and events on spaces and raw trackpad events. These are undocumented, so they require a lot of experimenting with. I feel Apple should support and create documentation for these APIs so users can develop UIs to use their own computers how they like.

  • You can activate the Robata UI by resting a finger on the corner of the trackpad. My thumb naturally rests on the bottom left corner of my external trackpad, so it's very natural to rest it there to open Robata. Also, the tactile feel of the corner of the trackpad makes it easy to find without thinking. An issue with the MBP built-in trackpad is that it is flush with the body of the laptop, so it takes more effort to find the corner. So, I added the ability to use cmd-{x}—where x is any char the user picks. This lets you open Robata with just your right hand on one half of the keyboard, or just your left hand on your trackpad. The key shortcut is also for mouse users.

  • The Robata app is around 1.8MB and has no dependencies (other than the macOS system libraries). It uses web views, which turned out to be very performant. As soon as the web views are no longer visible, they go into a sleep mode, reducing their RAM and CPU usage. Robata is not visible for most of the time, so this works well.

  • Some Swift developers have noted that text rendering using SwiftUI actually takes a lot of CPU when the text changes every second. I feel the optimizations built into web browsers make for a more performant UI in some cases. Also, the instant reload and Chrome dev tools make it much easier to develop with. You can change your text and see the re-render instantly, like painting a canvas. With SwiftUI, it is a minimum of a few seconds to recompile and navigate to the app state you are working on. Also, SwiftUI layout seems to be harder to debug than HTML/CSS, as the Chrome dev tools will show you why your elements are positioned where they are with flex/grid ruler overlays. SwiftUI has no debug tooling.

cssprinciples.com

  • I built this blog to teach CSS principles.
  • The CSS grid guide is probably the most interesting post to date: cssprinciples.com/3/
  • Both the static site and the examples are built with React/MobX.
  • The static site generator is a custom built script using Bun.js.
    • I chose Bun.js for its performance, it's particularly fast at hot reloading to preview changes instantly in the browser after an edit.
  • The content is written directly in JSX to maintain full control over HTML output, which is needed to allow maximum interactivity and customization in the future.
    • I avoided Markdown and MDX (JSX in Markdown) because:
      • The MDX compiler has less users than JSX; I think MDX based builds will be more likely to break in the future.
      • It seems more difficult to customize than just using JSX directly.

tabserve.dev

  • Gives developers an https url for their localhost webserver.
  • Enables them to share their local web server with the world, receive API webhooks, test on different devices and use https-only browser APIs.
  • More detail at tabserve.dev

bigWav

  • This is a privacy first transcription app.
  • It allows you to annotate your transcriptions and export them to PDF.
  • You can also save as .bigwav.

Technical details

  • Uses OpenAI whisper to convert audio to a transcript.
  • Whisper.cpp allows you to run the processing in the browser via WASM.
  • Uses Chromes File System API to read and write to files on the file system.
    • This is useful as the app has its own file format (.bigwav) which stores the audio along side the transcript and annotation.
    • When the user saves the file, it is written to their disk similar to how other local apps work.
  • Is an installable PWA.
    • This is useful for opening audio from the users file explorer.

Initial Instinct

You can learn more about the rules of the game at initial-instinct.com, tap "about".

Some technical details about the game:

  • Extracts peoples names from Wikipedia metadata and page content using Rust, inserting them into a SQLite database. The Wikipedia database is a single large XML file, so using Rust allows for faster iteration/changes as you can inspect the export results 10x faster.
  • Uses Node.js on the server, with SQLite, Google Container OS, Caddy for auto HTTPS.
  • Frontend uses React with Mobx.
    • The animations are videos as they were designed in Adobe After effects. To get a 1:1 replication it was easier to use the video's of the animations as 3d is hard to replicate using CSS animations.
    • This took a bit of effort to get running smoothly on iOS/iPhones as they try to prevent any caching to save battery, RAM and CPU. The wheel animations need to start at the exact correct moment, and iOS tries to lazy load them at the last moment, which can cause a multi second delay.

Stay 100

Stay 100 is a web app that shows you 100% of the stays for a given date range and location.

It is optimized for large screens by using all of the available space to show images.

At the time of launch, AirBnB paginates results making it difficult to quickly visually scan for interesting stays. This can mean missing stays because you did not look through all of the result pages. AirBnB also does not allow sorting or filtering by all fields (like rating, number of reviews or price).

Explained in more detail on https://stay100.madebyenzo.com.

Table Dog

Note To Web

  • Allows you to create a website from an Evernote notebook.
  • Includes a category system based on naming the notebooks with a tree path.
  • Static hosting with a CDN is used for extremely low latency serving of web pages.
  • Custom domains supported.

SQLite MPI

As part of Simple Job Tracker I needed to use SQLite, but the open source libraries available had dubious support for transactions and cross platform reliability.

In essence it uses Rust to cross compile a single code base, instead of re-implementing the same code in both Java and Objective C.

It turns SQLites FFI into a JSON RPC so that it can be used over the React Native bridge (the interface between the JS VM process and the native app process).

SMPI is described in much more detail at sqlite-mpi.madebyenzo.com.

Simple Job Tracker

SJT is a mobile app for Android and iOS that is intended to be used by small business owners.

Its designed for location based recurring service businesses. This means any business that visits many addresses per day, and will visit those same addresses on a recurring schedule in the future.

It manages invoice and receipt numbers, customer balances, customer contact details, upcoming jobs.

Data is synced to a remote store, allowing many users to use the app in a single business.

The SQLite MPI was designed as a result of this project.

SJT is described in much more detail at simplejobtracker.madebyenzo.com.

Tree merge

Tree merge is a tool I built in order to merge the category systems for all the DIY stores in the UK (see Everydiy).

It increased conversion rates by 100%.

The original issue was that when searching with vague terms, Elastic Search would return too many products.

Using search plus categories allows the user to very quickly drill down to what they are looking for.

Tree merge is described in much more detail at treemerge.madebyenzo.com.

Everydiy

Note:

  • This project was originally named "Lightcart".
  • Treemerge is a tool that was built for this project.

What is Lightcart?

Its a search engine for DIY products available on diy.com (B&Q), homebase.co.uk, screwfix.com and wickes.co.uk. The idea is to enable you to shop at all stores from one fast and focused interface. Once you have a list of items you want to purchase in your cart you are redirected to the suppliers site’s checkout page ready for you to pay.

The target users are people who have many things to purchase and want a quick and easy way to see what is available at all the DIY stores without jumping from tab to tab.

The name “Light” was chosen after the 3 attributes I wanted the application to have:

  • Fast
    • As in “lightening fast”.
  • Comprehensive
    • Light enables sight.
    • Seeing results from all suppliers.
  • Intuitive
    • As in “light entertainment”.
    • Easy to understand/use.

2010 research project revisited

This is the implementation of a research project I had for my degree dissertation in 2010 (titled “The possibilities and limitations of a meta shopping basket”). The original project was written in PHP with very little Javascript. Cross site XMLHttpRequests were sent with signed Java applets.

2015 technologies enabled a faster application

ReactJS/Javascript was used in a “single page” style application. ReactJS has a focus on fast DOM updates. The aim for the project was to keep application responses under 100ms.

A Chrome extension was used in place of a Java Applet to make the add to cart process much smoother.

Screenshots

Fast, Comprehensive, Intuitive
Fast, Comprehensive, Intuitive
Search
Search
Product page
Product page
Image gallery
Image gallery
Cart
Cart
Chrome extension
Chrome extension

transform-x

Technical details

  • This is a JS and WASM based library that works in any JS runtime.
  • JS runtimes include: Node.js, Bun, Deno, browser, Cloudflare workers.
  • Getting the same code base to work in all the JS runtimes was quite a challenge as they all have slightly different API's and have different security restrictions.
  • Can be used as a CLI, JS library or as an HTTP API.

lilo

Technical details

  • Uses Bun, a new JS runtime written in Zig that replaces Node and uses JavaScriptCore, Apple's JS engine for Safari.
  • This project was to test Bun out as a runtime.
  • The SQLite writes seem to have good performance, taking around 10ms for 1000 rows inserted.
  • SQLite is built into the Bun runtime, so there is no need for another build process.
  • In Node.js, better-sqlite3 requires compiling SQLite which takes around 30 seconds and complicates distribution.

Durafetch

What Cloudflare Durable Objects are.

Cloudflare Workers allow you to upload JS code to respond to HTTP requests that run in 200+ globally distributed datacenters. This reduces user-to-server latency to less than 50ms regardless of user location.

Edge hosting solutions do not handle state well as there is no central location.

In typical web servers state is stored in a central location. This is the simplest possible set-up (single web server, single database), but has a few disadvantages:

  • Will only have low latency for users close by. Latency can be 300ms for the opposite side of the world.
  • Must be always-on to handle those requests (a small VM is around $12/month).
  • Is a single point of failure.
  • You must manage the OS updates, CPU/RAM/disk sizing etc yourself.

Durable Objects are defined in JS classes that are bundled with a Cloudflare Worker. The worker acts as a proxy to receive public HTTP requests and then instantiates a Durable Object to pass the HTTP request to.

Durable Object's have these features:

  • A single identity
    • You can name a specific server instance with an ID and route requests to the same instance.
    • This allows you to keep JS state in memory.
  • Persistent storage.
    • A JS key value datastore, like localStorage that is written to disk.
  • WebSockets.
    • You can upgrade the HTTP request to a long-lived WebSocket for bidirectional communication.
    • This allows the WebSocket to interact with JS and KV stored state.

What Durafetch is.

Durafetch consists of two parts:

  • A CLI
  • Server functions you add you your Cloudflare Worker/Durable Object class.

A missing feature of Durable Objects is the ability to inspect and observe state outside of the JS API - you cannot access your data unless your write the HTTP endpoints yourself.

Durafetch:

  • Implements those HTTP endpoints
  • Implements a CLI to fetch the data from the endpoints, and then write them to a SQLite database

The two main use cases I have in mind are:

  1. Development

    • When developing you need to refresh and run code paths repeatedly as you modify the code. Not being able to see what the state of the storage is makes this very difficult as you have to either print the values to a console, or write your own solution to read the state.
    • Even if you can read the state, you often need to filter/query it. SQL allows you to do this.
  2. Production

    • Accessing and querying your server based state. E.g. How many sign-ups, sales. Usage based billing etc.

Why D1 cannot be used (for now)

Cloudflare have a SQLite based persistence API called D1 that looks good.

  • D1 is in Alpha, and looks like it will be in GA in around 12 months which is too faraway.
  • D1 is still distributed.
    • Each Durable Object gets its own SQLite instance, which means you will still need to join them into one central DB to do a global query.

Take a look

Durafetch is described in more detail at durafetch.madebyenzo.com or the Github repo.

Income Percentiles

Bezier Curves

People By Initials

The motive for this project is that there is currently no way to get a list of notable people via two character initials, which is essential to the DOMINIC system for memorizing two digit numbers in the book How to develop a perfect memory.

The people are listed by popularity. The data has been processed from Wikipedia using Python.

I also wanted to experiment with the Golang language from Google.

Angular JS was used for the application
Angular JS was used for the application

Lets Meet For Coffee

This was a side project I completed sometime in 2013 whilst living in Ho Chi Minh. The aim was to create a simple way for a prospect to arrange a quick meeting with me at a cafe that is convenient for them.

It is built on Google App Engine, Python, Backbone.js, Google Maps API and Bootstrap.

Screenshot