In this last article of this series, I want to summarize the development process of the Paint remake and critically reflect on the technology choices. Was this experiment successful?

Article Series

  1. Overview, Web Components & Architecture
  2. Canvas & Input
  3. Copy & Paste Images
  4. Accessing Files & File Handler
  5. Dark Mode Support
  6. Summary ⬅

Web Components

I decided to use Web Components for all UI components within the Paint remake. Apart from the basic Web Component functionality, the remake makes use of slots, CSS custom properties ("variables"), and scoped CSS. Web Components turned out to be very robust and work consistently across all evergreen browsers.

Furthermore, the Paint application is a Web Component itself. This means that you can easily embed it in other applications. During development, Paint was added to a demo project of a Web Components designer, where you can simply drag out the paint clone from the toolbox and play around with it.

Angular developers like me are very used to this concept: The application itself is an Angular component, and so are all other UI elements within the application.

Lit

During development, Lit, the successor of LitElement arrived. Lit provides several convenience functions for Web Component development. For example, plain web components neither have support for data binding nor implement a change detection concept. Lit introduces exactly this, which speeds up Web Component development by a lot and leads to smaller source files—at the cost of 5 kilobytes library size.

One of the promises for the new release of Lit was support for server-side rendering (SSR). Server-side rendering is beneficial for search engine optimization and can shorten the (perceived) loading time. Unfortunately, I was unable to find support for SSR in the new version. There is an experimental, separate package that adds SSR, but it's not production-ready yet. Once SSR support lands in the official version of Lit, I plan to implement server-side rendering for Paint.

Unfortunately, neither Web Components nor Lit provides any means of dependency injection. You can implement this functionality on your own with the help of events, which is what I did in this application. However, the resulting code is neither very readable nor easy to understand. I hope that Lit provides some helper utilities for dependency injection in the future.

All in all, I enjoy using Lit. As most frontend frameworks share the same basic concepts, learning Lit should be easy. Angular developers will notice many similarities: For example, there is the @customElement() decorator that's similar to @Component(), or the disconnectedCallback() lifecycle hook which behaves the same as ngOnDestroy().

Snowpack

The Paint remake is the first quasi productive project where I used Snowpack in. Snowpack is a build system that's less extensive than Webpack, but much faster in return.

Initially, I started the project with Snowpack 2, which turned out to be very robust. During development, Snowpack 3 arrived, which I quickly adopted. Unhappily, I hit many several bugs since the release of version 3, and the number of issues in Snowpack's issue tracker sharply increased. I currently need to pin Snowpack to an older version, as the dev build breaks in newer versions. This is due to a Snowpack bug in combination with CommonJS packages, an older package format which one of the remake's dependencies uses. The issue has not been resolved in a couple of months. Unfortunately, the older version has a problem with production builds, so that I need to fiddle around with the output files after the build.

In Angular, developers would typically split the HTML template, CSS styles, and TypeScript logic into different files. While Webpack implements a loader concept that allows you to modify the behavior when importing other files, Snowpack makes use of the standard ECMAScript module system which does not provide that. As a result, all component-related code needs to go into the same TypeScript file—comparable to React and Vue components. This way, the source files can become very long, even for simple components. With the advent of CSS module scripts, importing CSS files could become possible for ECMAScript modules in the future. Style preprocessors such as SCSS are not supported.

To summarize, Webpack is a lot more robust, but I really like Snowpack for its simplicity. If your project exclusively uses ECMAScript modules (including all dependencies), Snowpack is definitely worth a look.

JavaScript vs. TypeScript

Due to the lack of a TypeScript template for Snowpack and LitElement, I initially started the project with JavaScript—instead of TypeScript, which I use in my day-to-day work. Later, I migrated the project to strict-mode TypeScript. This process was quite tedious, and I found some subtle bugs in my JavaScript code. Now that TypeScript-based templates are available they are the go-to choice for any new Snowpack/Lit project. I recommend enabling TypeScript's strict mode to catch even more bugs during development.

In contrast to JavaScript, TypeScript offers support for interfaces. I created interfaces for tools and menu actions. The concrete tools and actions implement those interfaces.

Also, the project makes heavy use of code splitting via ECMAScript modules. The Web Components, models, menus, actions, and utilities are split into corresponding subdirectories for easier maintenance. Most of the time, I made use of the 1 class—1 file rule of thumb.

Modern Web Capabilities

The remake uses lots of modern web capabilities to add powerful features such as filesystem, clipboard, or local font access. If an API is not available on the target system, I use a fallback mechanism or disable the feature in the application. As most of the APIs are implemented as a part of Project Fugu, an initiative by Chromium contributors, most capabilities are available on Google Chrome, Microsoft Edge, and other Chromium-based browsers. Other APIs have already made their way into other browsers as well. It turns out that almost all features of Paint can be reimplemented on the web—with the sole exception of setting the current drawing as the desktop wallpaper, a niche functionality.

Paint Remake: A Successful Project

The Paint remake demonstrates that you can implement desktop-class productivity applications for browsers. I've used, tried, and tested web technology, such as the canvas element, combined it with more recent specifications (e.g., Web Components), and added cutting-edge capabilities to it. The resulting remake supports many of Paint's features. However, I didn't implement all tools and menu commands yet. I accept pull requests in the repository if you would like to collaborate. The best thing is that the Paint remake works across multiple browsers and operating systems—from Android to iOS, from Linux and macOS to Windows.

If you want to stay up to date, sign up for our newsletter, and we will inform you about new articles, webinars, and screencasts by our experts.

Related Articles

 | Christian Liebel

Article Series Overview, Web Components & Architecture Canvas & Input Copy & Paste Images Accessing Files & File Handler Dark Mode Support ⬅ Summary Desktop operating systems originally worked with a dark-on-light color scheme. In the meantime, most operating systems have added…

Read article
 | Christian Liebel

Article Series Overview, Web Components & Architecture Canvas & Input Copy & Paste Images Accessing Files & File Handler ⬅ Dark Mode Support Summary Paint is a traditional productivity app using a file-based workflow: Typically, you create a new drawing within the application and…

Read article
 | Christian Liebel

Article Series Overview, Web Components & Architecture Canvas & Input Copy & Paste Images ⬅ Accessing Files & File Handler Dark Mode Support Summary In the previous part of this series, I showed how you can use the HTML element to implement the drawing area. In order to copy…

Read article