Categories
Releases Speaking

Introducing Rety: live coding, without the stress

I recently spoke at CSS Day in Amsterdam. It was only my second f2f talk after the pandemic. It went down really well, both in person, and recently that the video was released:

Here is a sample of tweets about it that made me particularly warm and fuzzy inside:

There’s a lot more where these came from too.

This was not just my second post-pandemic talk, but my first talk using Rety, which is what this post is about.

As you may know, I love live coding as a teaching tool, and over the years it has become part of my trademark speaking style.

When combined with some kind of interactive preview, it allows the speaker to demonstrate not only the final state of a coding snippet, but also how you get there, and what the intermediate results are. Live coding is to programming what a blackboard is to math or physics.

But it does create a unique challenge: My live coded slides don’t make sense without me. This may be acceptable for a conference talk, which is usually recorded, but not in other contexts, such as teaching a university course, where all instructors need to be able to teach all lectures, and students need to be able to quickly refer to examples shown.

Back in the fall of 2021, when we were preparing for the second iteration of our course, Design for the Web: Languages and User Interfaces, this came up as a pressing issue. The current state of the course required me to be there to teach my lectures, and this may well be the last year I teach it, since I’m finishing up my PhD soon.

I didn’t want to completely remove live coding from my slides, as I truly believe it is the perfect implementation of the “show, don’t tell” teaching adage for certain things, so I thought instead: what if I could record my live coding, and make it replayable?

Doing so manually seemed like cruel and unusual punishment. And thus, Rety was born (pronounced like the “rety” in “retype”).

While originally the plan was for me to still live code, and have the Rety functionality there for students and future instructors, I ended up using it during my own lectures as well, as I concluded that a well crafted Rety script was strictly superior to me doing the live coding:

  • Same progressive development as a live demo
  • It still affords unplanned demonstrations (e.g. to answer a question), since Rety still works with the same editors, and I could always pause it and take over if needed.
  • I could record myself and edit the script to maximize education value and minimize typos, delays, fumbling etc.
  • People can consume typed text far faster than people can type text. This is why most video tutorials speed up the typing. With Rety, typing speed is adjustable, and doesn’t need to match mine.

After test driving it for our course the entire spring 2022 semester, it went through the ultimate test in June 2022: I used it for my CSSDay conference talk. You can watch the talk here (first live demo at 7:15).

Right now Rety is just a set of two classes: Recorder and Replayer, which are used entirely independently. The exact UI is left up to the Rety user. E.g. to use it in my slides, I integrated it with the Live Demo plugin of Inspire.js (it is automatically included if a <script class="demo-script" type="application/json"> is found in a live demo slide).

The library could use more docs and some tests and I have doubts about the API, but I figured I should release it it earlier rather than later (it’s already been sitting in a repo for 7 months). After all, what best time to release it than when the first Rety talk is still making the rounds?

My vision is to ultimately evolve and standardize the Rety script format, so that it can be used to describe a coding interaction across a variety of tools. There are so many possibilities!

  • Wouldn’t it be cool if CodePen and similar playgrounds supported embedding a Rety script into a pen?
  • What if you could store Rety scripts in a repo and editors like VS Code recognized them and let you replay them?

Enjoy: Rety

Categories
Original Releases

Releasing Color.js: A library that takes color seriously

Related: Chris’ blog post for the release of Color.js

This post has been long overdue: Chris and I started working on Color.js in 2020, over 2 years ago! It was shortly after I had finished the Color lecture for the class I was teaching at MIT and I was appalled by the lack of color libraries that did the things I needed for the demos in my slides. I asked Chris, “Hey, what if we make a Color library? You will bring your Color Science knowledge and I will bring my JS and API design knowledge. Wouldn’t this be the coolest color library ever?”. There was also a fair bit of discussion in the CSS WG about a native Color object for the Web Platform, and we needed to play around with JS for a while before we could work on an API that would be baked into browsers.

We had a prototype ready in a few months and presented it to the CSS WG. People loved it and some started using it despite it not being “officially” released. There was even a library that used Color.js as a dependency!

Once we got some experience from this usage, we worked on a draft specification for a Color API for the Web. In July 2021 we presented it again in a CSS WG Color breakout and everyone agreed to incubate it in WICG, where it lives now.

Why can’t we just standardize the API in Color.js? While one is influenced by the other, a Web Platform API has different constraints and needs to follow more restricted design principles compared to a JS library, which can be more flexible. E.g. exotic properties (things like color.lch.l) are very common in JS libraries, but are now considered an antipattern in Web Platform APIs.

Work on Color.js as well as the Color API continued, on and off as time permitted, but no release. There were always things to do and bugs to fix before more eyes would look at it. Because eyes were looking at it anyway, we even slapped a big fat warning on the homepage:

Eventually a few days ago, I discovered that the Color.js package we had published on npm somehow has over 6000 downloads per week, nearly all of them direct. I would not bat an eyelid at those numbers if we had released Color.js into the wild, but for a library we actively avoided mentioning to anyone outside of standards groups, it was rather odd.

How did this happen? Maybe it was the HTTP 203 episode that mentioned it in passing? Regardless, it gave us hope that it’s filling a very real need in the pretty crowded space of color manipulation libraries and it gave us a push to finally get it out there.

So here we are, releasing Color.js into the wild. So what’s cool about it?

  • Completely color space agnostic, each Color object just has a reference to a color space, a list of coordinates,, and optionally an alpha.
  • Supports a large variety of color spaces including all color spaces from CSS Color 4, as well as the unofficial CSS Color HDR draft.
  • Supports interpolation as defined in CSS Color 4
  • Doesn’t skimp on color science: does actual gamut mapping instead of naïve clipping, and actual chromatic adaptation when converting between color spaces with different white points.
  • Multiple DeltaE methods for calculating color difference (2000, CMC, 76, Jz, OK etc)
  • The library itself is written to be very modular and ESM-first (with CJS and IIFE bundles) and provides a tree-shakeable API as well.

Enjoy: Color.js

There is also an entire (buggy, but usable) script in the website for realtime editable color demos that we call “Color Notebook”. It looks like this:

And you can create and share your own documents with live Color.js demos. You log in with GitHub and the app saves in GitHub Gists.

Color spaces presently supported by Color.js
Categories
Articles

Mass function overloading: why and how?

One of the things I’ve been doing for the past few months (on and off—more off than on TBH) is rewriting Bliss to use ESM 1. Since Bliss v1 was not using a modular architecture at all, this introduced some interesting challenges.

Categories
Tutorials

Writable getters

Setters removing themselves are reminiscent of Ouroboros, the serpent eating its own tail, an ancient symbol. Media credit

A pattern that has come up a few times in my code is the following: an object has a property which defaults to an expression based on its other properties unless it’s explicitly set, in which case it functions like a normal property. Essentially, the expression functions as a default value.

Categories
Tips

Introspecting CSS via the CSS OM: Getting supported properties, shorthands, longhands

For some of the statistics we are going to study for this year’s Web Almanac we may end up needing a list of CSS shorthands and their longhands. Now this is typically done by maintaining a data structure by hand or guessing based on property name structure. But I knew that if we were going to do it by hand, it’s very easy to miss a few of the less popular ones, and the naming rule where shorthands are a prefix of their longhands has failed to get standardized and now has even more exceptions than it used to. And even if we do an incredibly thorough job, next year the data structure will be inaccurate, because CSS and its implementations evolve fast. The browser knows what the shorthands are, surely we should be able to get the information from it …right? Then we could use it directly if this is a client-side library, or in the case of the Almanac, where code needs to be fast because it will run on millions of websites, paste the precomputed result into whatever script we run.

Categories
Tips

Import non-ESM libraries in ES Modules, with client-side vanilla JS

In case you haven’t heard, ECMAScript modules (ESM) are now supported everywhere!

While I do have some gripes with them, it’s too late for any of these things to change, so I’m embracing the good parts and have cautiously started using them in new projects. I do quite like that I can just use import statements and dynamic import() for dependencies with URLs right from my JS, without module loaders, extra <script> tags in my HTML, or hacks with dynamic <script> tags and load events (in fact, Bliss has had a helper for this very thing that I’ve used extensively in older projects). I love that I don’t need any libraries for this, and I can use it client-side, anywhere, even in my codepens.

Once you start using ESM, you realize that most libraries out there are not written in ESM, nor do they include ESM builds. Many are still using globals, and those that target Node.js use CommonJS (CJS). What can we do in that case? Unfortunately, ES Modules are not really designed with any import (pun intended) mechanism for these syntaxes, but, there are some strategies we could employ.

Categories
Articles

Refactoring optional chaining into a large codebase: lessons learned

Chinese translation by Coink Wang

Now that optional chaining is supported across the board, I decided to finally refactor Mavo to use it (yes, yes, we do provide a transpiled version as well for older browsers, settle down). This is a moment I have been waiting for a long time, as I think optional chaining is the single most substantial JS syntax improvement since arrow functions and template strings. Yes, I think it’s more significant than async/await, just because of the mere frequency of code it improves. Property access is literally everywhere.

Categories
Tips Tutorials

Hybrid positioning with CSS variables and max()

Notice how the navigation on the left behaves wrt scrolling: It’s like absolute at first that becomes fixed once the header scrolls out of the viewport.

One of my side projects these days is a color space agnostic color conversion & manipulation library, which I’m developing together with my husband, Chris Lilley (you can see a sneak peek of its docs above). He brings his color science expertise to the table, and I bring my JS & API design experience, so it’s a great match and I’m really excited about it! (if you’re serious about color and you’re building a tool or demo that would benefit from it contact me, we need as much early feedback on the API as we can get! )

For the documentation, I wanted to have the page navigation on the side (when there is enough space), right under the header when scrolled all the way to the top, but I wanted it to scroll with the page (as if it was absolutely positioned) until the header is out of view, and then stay at the top for the rest of the scrolling (as if it used fixed positioning).

Categories
Rants

Today’s Javascript, from an outsider’s perspective

Today I tried to help a friend who is a great computer scientist, but not a JS person use a JS module he found on Github. Since for the past 6 years my day job is doing usability research & teaching at MIT, I couldn’t help but cringe at the slog that this was. Lo and behold, a pile of unnecessary error conditions, cryptic errors, and lack of proper feedback. And I don’t feel I did a good job communicating the frustration he went through in the one hour or so until he gave up.

Categories
Articles

ReferenceError: x is not defined?

Today for a bit of code I was writing, I needed to be able to distinguish “x is not defined” ReferenceErrors from any other error within a try...catch block and handle them differently.

Now I know what you’re thinking. Trying to figure out exactly what kind of error you have programmatically is a well-known fool’s errand. If you express a desire to engage in such a risky endeavor, any JS veteran in sight will shake their head in remembrance of their early days, but have the wisdom to refrain from trying to convince you otherwise; they know that failing will teach you what it taught them when they were young and foolish enough to attempt such a thing.

Despite writing JS for 13 years, today I was feeling adventurous. “But what if, just this once, I could get it to work? It’s a pretty standard error message! What if I tested in so many browsers that I would be confident I’ve covered all cases?”

I made a simple page on my server that just prints out the error message written in a way that would maximize older browser coverage. Armed with that, I started visiting every browser in my BrowserStack account. Here are my findings for anyone interested:

  • Chrome (all versions, including mobile): x is not defined
  • Firefox (all versions, including mobile): x is not defined
  • Safari 4-12 : Can't find variable: x
  • Edge (16 – 18): 'x' is not defined
  • Edge 15: 'x' is undefined
  • IE6-11 and Windows Phone IE: 'x' is undefined
  • UC Browser (all versions): x is not defined
  • Samsung browser (all versions): x is not defined
  • Opera Mini and Pre-Chromium Opera: Undefined variable: x

Even if you, dear reader, are wise enough to never try and detect this error, I thought you may find the variety (or lack thereof) above interesting.

I also did a little bit of testing with a different UI language (I picked Greek), but it didn’t seem to localize the error messages. If you’re using a different UI language, please open the page above and if the message is not in English, let me know!

In the end, I decided to go ahead with it, and time will tell if it was foolish to do so. For anyone wishing to also dabble in such dangerous waters, this was my checking code:

if (e instanceof ReferenceError 
    && /is (not |un)defined$|^(Can't find|Undefined) variable/.test(e.message)) {
    // do stuff
}

Found any cases I missed? Or perhaps you found a different ReferenceError that would erroneously match the regex above? Let me know in the comments!

One thing that’s important to note is that even if the code above is bulletproof for today’s browser landscape, the more developers that do things like this, the harder it is for browser makers to improve these error messages. However, until there’s a better way to do this, pointing fingers at developers for wanting to do perfectly reasonable things, is not the solution. This is why HTTP has status codes, so we don’t have to string match on the text. Imagine having to string match “Not Found” to figure out if a request was found or not! Similarly, many other technologies have error codes, so that different types of errors can be distinguished without resulting to flimsy string matching. I’m hoping that one day JS will also have a better way to distinguish errors more precisely than the general error categories of today, and we’ll look back to posts like this with a nostalgic smile, being so glad we don’t have to do crap like this ever again.