SolitaireCat.com game design

A cat-themed Solitaire playing Website and Solitaire history blog.

last updated: Apr 11, 2024

SolitaireCat.com is a cat-themed Solitaire playing website and Solitaire history blog, because, why not?

I developed the site as a project to improve my Typescript skills and get some exposure to SolidJS and Supabase. A game seemed like a fun enterprise to achieve these goals, and online Solitaire feels like the yardstick of the genre.

Design goals and game features

Game

  • Move hinting
  • Undo / redo

Visuals

  • HTML / SVG for all visual elements - i.e. all vector
  • Smooth cat-themed animations for card movement, hinting, drop, invalid moves, win celebration etc.

Accounts/backend

  • User accounts with usernames, email, password resets etc. all in game.
  • Player profiles with ranks, best games etc.
  • User game preferences
  • Load / save games
  • High scores

Canvas vs. DOM

The game itself runs entirely on the client and is written in vanilla Typescript and CSS.

I don’t have any experience using Canvas, but my base assumption was that a game like this is far more easily implemented using the DOM. The DOM gives us animations and rendering for free. Flipping and moving cards around is as simple as applying the appropriate CSS transformations. This may not be true for an experienced Canvas developer or if using a framework, but my DOM/CSS:Canvas knowledge ratio is ∞.

It’s not a demanding game and SVG/CSS animation is plenty performant.

Minimizing the size of the SVG cards

A full deck of dynamically generated SVG cards in 47 KB.

The SVG cards are generated dynamically from a set of SVG primitives. See here for a separate article here detailing the process.

SVG animation

Beyond the cards, there are various animations included, such as the cat advisor shown below. By using SMIL animation to animate the paths and transforms, smooth animations can be produced without adding too much to the page weight.

Technology stack

The Solitaire game itself runs entirely on the client and is written in vanilla Typescript.

The back-end (user accounts, saved games, etc.) is hosted on Supabase, with all data stored in PostgreSQL and Supabase edge functions used to validate game results and perform some account maintenance tasks.

SolidJS is used on the client to manage user and user preference state, and to render the various modal windows (e.g. leaderboards, saved games, user profiles, etc.).

For the accompanying blog, it’s generated by Hugo and uses the same CSS framework that I use for this blog, although it’s not much of a framework, just a few SCSS files that I use as the base for most client-side web projects.

It’s hosted on Cloudflare Pages and Cloudflare Workers are used to send email from the contact us form.

Reducing the page weight

The only runtime dependencies of the app are SolidJS and SupabaseJS. Solid is very efficient, supports tree-shaking, and only adds ~7 KB to the app. Supabase on the other hand…

Supabase doesn’t support tree shaking

Supabase is implemented using classes, and so mostly doesn’t support tree shaking. The Javascript client includes a lot of functionality that I don’t use. Things like the realtime API, storage API, and a fetch implementation.

Version 2.39 breaks down as follows:

Component gzip
gotrue 25 KB
realtime 15 KB
postgrest 10 KB
storage 7 KB
supabase 5 KB
functions 2 KB
node-fetch 1 KB
total 65KB

Bundle breakdown for SupabaseJS client v2.39

A simple program that uses the Supabase JS client with no method calls, like the following:

export { createClient } from "@supabase/[email protected]";
const client = createClient("url", "key");

Results in a gzip bundle size of 25 KB.

To reduce this, I forked supabase-js, gotrue-js, functions-js and postgrest-js and did the following:

  • Removed realtime/storage imports/exports.
  • Removed realtime/storage methods on the client.
  • Removed fallback fetch / custom fetch capability.

This reduced the gzip size from 25 KB to 14 KB. (raw 96 KB -> 54 KB).

The final JS size is 50 KB (gzip)

I estimate the SolidJS runtime is ~7 KB, and my game code / card gen is 29KB:

Sizes (KB) gzip
Supabase 14 KB
SolidJS 7?KB
Game 29 KB
Total 50 KB
Inlining SVG symbols into HTML

The game uses various small SVG elements, such as crossed paw effects, cat head popcorn, menu symbols etc.

There are some arguments for keeping these as separate files/resources for caching, easier maintainence etc., but: one 5 KB request is better than five 1 KB requests, the HTML is cached anyway, won’t change often, and is the only page using them, they can be kept separate and inlined on build anyway, and making all the preceding points moot, they need to be inlined regardless in order for gradients to work (see here and here).

Vite provides the transformIndexHtml in its plug-in API, which in conjunction with Node’s file system API, can easily be made to inline the SVG into the index.html document.

All of the SVG content is inlined, with the exception of the card backs, since most go unused.

The gzipped and minified HTML file after SVG inlining is 19 KB.

The final game is 148 KB all in
Sizes (KB) gzip
SVG cards 46 KB
HTML 19 KB
CSS 8 KB
images* 25 KB
JS 50 KB
Total 148 KB

* Card backs and table felt. Other SVG images are inlined on the HTML.

Theres always room to reduce further, but the marginal returns by that point didn’t warrant it.

That is all

So that’s how SolitaireCat.com came about. Please take a look, play a hand, and let me know if anything is not working as it should be.

Appendix: Stack summary

Front-end

  • Vanilla Typescript
    Gameplay, animations.

  • SolidJS
    User and user preference state, rendering the various modal windows (e.g. leaderboard, user profile, etc.).

  • SupabaseJS
    JS interface to Supabase Postgres / Edge functions backend.

Back-end

  • Supabase edge functions (Typescript)
    Validation of game data, account maintenance tasks.

  • Supabase Postgres (PL/pgSQL)
    User account storage, user profiles, saved games, leaderboards.

  • Cloudflare workers (Typescript)
    Contact us email submissions.

  • Cloudflare pages
    Static hosting.