Cool! Hi, everyone! Let’s talk about Houdini! So the first question, the obvious question,
is: What is the CSS-TAG Houdini Task Force? Well, according to their wiki, the idea is
to jointly develop features that explain the magic of styling and layout on the web. The magic of being able to do that. Practically, though, what does that mean? It means extending CSS via JavaScript. So that authors, us, no longer have to wait
decades for standards bodies to come up with something new and implement it in-browser. But wait! You might say. Can’t we do this already? PostCSS does this. Lots of JavaScript that extend CSS. Well, not quite. It’s currently not possible to actually extend
JavaScript. Or CSS. Through JavaScript. It’s only possible to write JavaScript that
mimics other things with the CSS we have available today. Actually, polyfilling CSS, like bringing the
new awesome CSS grid spec in, is hard if not impossible to do in a way that’s just not
terrible for your user’s performance. Houdini is going to let authors actually tap
into render engines. And we’re gonna talk about the CSS render
engine today. And allow us to do it at CSS engine speeds. And that’s the difference. That’s what really makes this really cool. So much like service workers are a low-level
JavaScript API for the browser’s cache, I’ve started to think about Houdini as a low-level
set of JavaScript APIs for the browser’s render engines. And like I said, that’s really cool. Before we get started, a couple notes. First, a big thanks to two people in particular. One is Tab Atkins. He is a spec hacker at Google. And about four years ago, at SassConf, he
showed me — he wrote a little Houdini spec for me, and that got me into this whole thing. I’m not actually part of the Houdini Task
Force. I just like it. And the other one is Serma, who’s been answering
all the questions I have about Houdini over the past few weeks, while I’ve been preparing
— updating this talk, I should say, because some of this stuff changes pretty fast. So the first question everyone is going to
ask once you see all this cool stuff is: Can I use Houdini? And the answer, unequivocally, is no. (applause) You absolutely can’t. The theme of this conference is: Problems
of today, solutions of tomorrow. This is a solution of tomorrow. Now, there’s a lot of interest in implementing
Houdini. The Chrome team is kind of further every ahead. Canary, specifically. There’s a lot of interest in typed OM. A couple weeks ago, Safari signed on with
intent to implement typed OM. But this is not available anywhere. And as such, a warning for this talk. Everything that you’re about to see is very
early days in progress. Syntax not final. Some of this stuff has changed so many times
over the past couple weeks that I’m literally afraid to update Canary, in fear of my demos
breaking. I have given a form of this talk over the
past year, and over the past year, there have been four API-incompatible breaking changes. Some of these docs that I’m gonna show you,
some of these specs, literally were updated four days ago. So… Some of this is speculative. Some of this might not work as the final syntax. Terms and conditions apply. You don’t get your money back for this. Your mileage may vary. But anyway… Let’s talk about it anyway! Because it’s lots of fun! The first and the really cool bit is worklets. Worklets are like itty-bitty web-ish workers. Worklets allow us to actually extend render
engines. They’re kind of like web workers, but they
work with a very, very isolated scope, to the point where they don’t have access to
self or this. They’re designed to be parallelized, and run
on multiple threads. And they live on the global scope for the
render engine to use, not for us as authors to use. It looks a little bit something like this. We have our window object in our browser,
JavaScript, and we have a worklet, and then we can add a module. Add module will go point to a local script,
kind of like how service workers work, and then it will load that module. But instead of loading that module into our
main thread, it’s going to load it into different threads, separate threads, that are specifically
worklet threads. The worklets will be loaded into two or more
worklet threads — again, separate from the main thread. Separate from where our JavaScript runs. And then the browser will call them as needed. Add module is also a promise. So every time we add a module, we can then
off of it, so we can work with worklets after they’ve been loaded. And one of the examples I’m gonna show later
on shows why that’s important. The actual worklet itself is a script that
looks a little bit like this. There’s a function that’s register whatever
the worklet name is, with a name that you give it, and then a class. A JavaScript class. Each worklet is going to define one or more
process methods for that class. Process, the name of the actual process, will
depend on the worklet. And that’s the thing that the browser, the
render engine, is going to invoke when it wants to use that worklet. It can take in custom or optional arguments,
might return stuff, might just work directly on the objects that get passed in. Kind of depends on the worklet. So let’s look at the life cycle. We start with the render engine. Our render engine has a main thread and then
it also spins up worklet threads. All these worklet threads are separate threads,
isolated from the main thread. That means they don’t affect the main thread’s
performance. If there’s an error, they don’t affect the
main thread. From our main thread, our browser JavaScript
gets called. Our browser JavaScript will go run whatever
we want it to run. And as part of that, we can invoke worklet.addModule. If we invoke worklet.addModule, it’ll grab
that worklet and load that worklet — instead of loading it into the main thread, it loads
it into, like I said, two or more of these worklet threads. Finally, when we want to go and actually invoke
it, the render engine will call whatever that process method is on our worklet, to go and
run it from one of the worklet threads. Worklets are the underlying foundation for
which all of Houdini is based. They’re the magic that makes it happen. They are Houdini’s secret sauce. The next big thing that Houdini introduces
is the typed OM. The new hot list. Or big CSS, if you like it that way. From their wiki, converting CSS object module
values or strings, I should say, into meaningfully typed JavaScript representations and back
incurs a very large overhead. This specification exposes CSS values as typed
JavaScript objects to facilitate their performant manipulation. The typed OM — it provides this new structure,
this new class for JavaScript that we can actually use to understand our CSS representations
meaningfully. And this new class is the CSS style value
class. And it has a bunch of subclasses. It has the CSS keyword subclass, which is
our idents, our inherits, and stuff like that. There’s position values, which provide us
with an X and a Y. There are transform values and its subclasses
like skew and rotate for transforms, there’s unit items which is either a raw number, or
a raw number with a percentage. Basic numbers, basically. And then there’s CSS math value. Which is kind of more complex numbers, things
like calcs and mins and maximums and sums. And what a CSS math value does is eventually
all that stuff should come down to a CSS unit. Should be calculatable down to a CSS unit. But they’re kind of more complex than just
a single unit value. So again, it looks a little bit something
like this. This spec was updated August 29th, 2017. So this doesn’t even work in Canary yet. It just returns undefined, which is fun. But what we have here is we have an example
class with a background position, center, bottom, 10 pixels. We can all intuitively, if we’ve worked with
CSS, figure out where that lines up. We can get the typed OM representation of
that by grabbing the element and then grabbing its attribute style map. That attribute style map is the typed OM. And then we can get off of that, so if we
get the background position’s X, we will get a CSS percentage that is 50. Because it’s centered. If we get its Y, we get a CSS math value that’s
a CSS percentage. It’s a math value that’s a sum of 100% and
-10 pixels. That brings it to the bottom and scoots it
up 10 pixels. So we now have a typed representation of the
actual position that this item is in. The typed OM provides an underlying foundation
to meaningfully connect our CSS and our worklets. If worklets are Houdini’s secret sauce, then
the typed OM are the chicken nuggets, keeping our fingers dry. And mostly it’s that, because I really wanted
to use this GIF. And I couldn’t think of a better analogy using
this GIF. That’s fine. But yeah. What can I do with all this? Is probably the question you’re asking. And the answer is: You can do real rad stuff
with this. So now we get to go play the live demo game. Let’s talk about part one, the cool custom
stuff. Please allow me to introduce you to window.css. Window.css becomes our new home for all the
fabulous things that we can do with Houdini. So we start with CSS variables. Which we really, really need to start thinking
about as custom properties. And the reason we need to start thinking about
them as custom properties is: The current version of the spec will let us finally make
snozzberries taste like snozzberries. What does that mean? Well, the current state of the world is we
have myColor, which is a CSS custom property or CSS variable, if you’ve heard of it that
way. We set it to green. Then we have… We reset it to a URL. And it’s… URLs aren’t a color. So when we go and try and use this in our
color, everything is sad, because URL is not a color, and these are stupid variables, and
they don’t know any better. But then… We can actually register this property. So window.css.registerProperty allows us to
say: Look, I have this property. It’s called this. Its syntax is this. Now that’s definitely a color, because we
told our render engine that this is a color. And because we know it’s a color, that URL
junk — it gets skipped! Because URLs aren’t colors! So the full kind of syntax for registerProperty
is you have a name that’s a string. You have the syntax, which is a couple different
options. I’m gonna show you what those are in a second. And it defaults to allowing everything in. You can choose whether or not this property
inherits up the DOM. And you can also set an initial value, if
you’d like. So let’s take a look at what available syntaxes
there are. There are length syntaxes. There’s number. There’s percentage. There’s length percentage, which are things
like calc. There’s colors, images, URLs. There are integers, angles, times. There are resolutions, there are transform
lists. And there are idents. You can also combine syntaxes in multiple
ways, to really create custom properties. You can have an individual property. You can take a property that takes one or
more different types, using the pipe for OR. You can have idents like big, bigger, all
caps bigger, so if you want an ident that’s awesome, you can literally write awesome,
and then you’re okay. Or you can have lists by appending a plus
to the type that you’re looking at. This is the live demo. So we have registered a property. Its syntax is color. We’re not gonna have it inherit up the tree. And its initial value we’re gonna set to Rebecca
Purple. So we have this pretty button. One of the advantages of registering properties
is once you’ve registered a property, the render engine now understands how to transition
this property, because it understands what the type is. So you see now if I hover over this register
button, it transitions. Nice. Pretty. But it gets (audio dropped) if we change from
coffee to red… Red works. And I can transition. But even better, if I copy this URL, and again,
URL is not a color… If I go and change registeredColor to that
URL, you’ll see it defaults to Rebecca purple. Because registeredProperty doesn’t have a
valid color in it, so it falls back to whatever the initial value is. This really, truly allows us to create custom
properties, which is fabulous. The second thing we’re gonna talk about is
CSSPaint. Every day is a good day to paint. Especially when you’re painting in CSS. Have you ever wanted to use Canvas as a background
image or a border image in CSS? And I know the answer for pretty much all
of you is no. But trust me — it’s really cool when you
start to think about it. Especially when you can do that with all the
styling ease of an element and the scalability of an SVG. Put that all together, and you basically have
what the Paint API does. Let’s take a look at the paint worklet. So this is the first thing that we’re doing
with a worklet. There are three potential statics that you
can use. One is importProperties. And that will actually look at the element’s
definition for the properties that you’re looking for and bring them in for us to use
as typed OM. There are there’s arguments. So you can optionally include arguments when
you pass to the new paint function. And those are the same typed syntax that we
have when we’re registering properties. And we can optionally choose whether or not
we want an alpha. The process for the paint worklet is the paint
function. This paint function takes in context, which
is a Canvas-like drawing context. Size, which is the size of the box we’re painting
in. Props, which are those props that get imported. And arguments, which are the arguments that
we import. And with all that, we can actually start to
draw as part of CSS. So we’re gonna look at this demo. It’s a paint… It’s a circle paint worklet. We’re gonna draw a circle, centered in our
box. So we’re gonna look for the circle color property. We are going to get the circle color property
from our props. And this is a CSS style value. This is our typed OM. We’re then gonna do a little bit of drawings. We’re gonna do some math to figure out the
position of the circle and the radius. And then we’re gonna start to draw. And if you’ve ever used Canvas drawing, this
looks very similar. We’re gonna begin our path. We’re gonna draw an arc, which will draw our
circle. We’re going to set the fill style to the actual
color that’s brought in, and then we’re going to fill it all in. Finally, to actually use this, window.css
— there it is again — paint worklet. AddModule. Path to our worklet. And here we get our second live demo. We have this circle that’s painted in the
background of our text area. If I increase the size, move it around, it’s
a circle. By changing this property… Changing it to blue… Changing it to yellow. Changing it to green. All of that changes the actual paint based
on properties I have. Now, we can do some more interesting things. Like drawing faces. And those faces, like I said, scale and because
I’m really great at art, get a little bit dopey. But you’ll notice that the eyes never actually
come together. They always stay right there. So we can draw and we can change all of these. Oh, maybe I spell blue right… Right from our CSS. So we’re drawing these complex shapes from
our render engine at render engine speeds, powered by the properties that we have in
CSS. Now, we can even do some really crazy things. Like draw warning signs. Because you can’t use Houdini yet. In case you forgot. And we can style these, just like we would
style our circle. So we have this ability to create really complex
images in CSS, without the need of running it in our main thread. Okay. So… Everything so far has been mostly stable. From here on out, things get a little fuzzy. The next item up is CSS animation API. Because yo dawg, I heard you like Parallax. What the CSS animation API lets us do is listen
for user input like scroll and touch events, and change styling based on that user input. And it does this all off the main thread,
which means no more blocking user input so that you can have that parallax effect that
you like so much. And the animation API is what’s going to make
things like parallax perform. So if we take a look at the animation class,
it’s fairly straightforward. There’s a constructor, because we’re gonna
be creating new animator instances. Constructor gets called whenever a new instance
gets created. And then there’s the animate process, that
takes in the current time of our timeline, because we’re using animations. And a group of effects. That group of effects are going to be the
actual animation effects that we want to apply, as part of this animation that we’re building. So let us take a look at an example of this
for a Twitter header. Twitter header being that bar that fades in,
and the avatar that kind of gets small as you scroll. Our constructor — we’re gonna create a new
cubic bezier easing function. We have a clamp function, which is going to
just be internal for us to find the smallest of the largest thing. And then we have our animate function. And the animate function is pretty straightforward. We get our scroll. Our scroll is equal to the current time in
our timeline. And then we manipulate the time of the effects
that we’re working on. So our first one — we’re just gonna set the
timeline equal to whatever the scroll is. And our second one, we’re gonna set it to
an easing function result of whatever that clamp function is. And this is what allows us to kind of control
the timeline of our actual keyframes. And that’s important, because we’re dealing
with keyframe animations now. Our HTML looks like this. We have a scroll container. This is the thing we’re scrolling inside. We’ve got our little header bar. And we’ve got our little avatar with some
extra text, some extra content. The reason this is important is how the animation
API actually takes hold. And that’s with a promise. So after we add our animation worklet as a
module, now I don’t know why this isn’t on window.css. I don’t know. The spec doesn’t have it there. When the spec does have it there, it does. Maybe this will be on window.css in the end. Maybe it won’t be. Who knows? But the key thing here isn’t whether it’s
on window.css. The key thing here is that: Once our worklet
is loaded, we instantiate a new worklet animation. A new worklet animator. And we’re going to call the name of the worklet
that we want it to use the name of the animation function we want it to use. There are two arguments that get passed in
here. One is an array of effects. And this is that effects argument that’s back
in our worklet. This is an array of keyframe animations. And this comes — or keyframe effects — which
come from the web animations API. So the first argument in a new keyframe effect
is the element you want to work on. The second is an array of effects you want
to perform. So our first one we want to perform on our
header. And we want it to scale from zero to 0.5. Right? Yeah. And then we want to set its duration. Now, we are actually, as part of the animation
API, feeding the timeline, the animation timeline, directly into the keyframe effects. So we don’t want any duration. We want a 1 millisecond duration with no iterations. We want this to go frame by frame based on
the timeline we pass in. Sorry, that’s the avatar, the first one. The second one is our header, where we do
the same type of setup with our keyframe effect. Opacity 0 to 0.8, I think I have it set at,
and then the same duration. The second input that we have for our new
animator, the new animation animator, is the scroll timeline. Now, this is something new. They’re working on other user inputs, like
touch and like pointer events. But what the scroll timeline is — is: Listen
to scroll events, and then update your timeline from that scroll event. So we’re gonna take our scroll container,
which is the thing we’re listening inside. What we want the actual timeline to be, so
we want it to go to 1, we want it to start at zero, and we want it to end at the offset,
the height offset, of the element. And this will let us control how our timeline
is, what our timeline is, what it’s looking for, as we scroll. Sometimes it’s a little bit easier to explain
in pictures. So let’s do that. We have our whole thing set up. We have our header element, which is a keyframe
effect attached to it. We have our avatar element, which has a keyframe
effect attached to it. And then that red scroll bar is our current
time. It’s the position in our timeline. As we scroll down, you’ll see that the keyframe
effects change the look of the header and the avatar, based on that position, based
on that current timeline position, which comes from that scroll timeline. We can dive in, and we can see an actual example
of this, using a demo that we’ve put together. As we scroll, you’ll see that header starts
to fade in and the avatar starts to get small. And as we scroll, that’s opacity in, header
small, and it’ll happen in reverse as well. And again, because this is a worklet, all
of this work is being done off of our main thread, which means it’s not affecting our
user input, it’s not affecting our performance. Everything is happening kind of in its own
isolated space. Which is exactly what we want for this type
of effect. The fourth and final worklet we’re gonna talk
about is a little bit of a doozy. It’s called the CSS layout API. And it lets us start to play Tetris with our
layouts. The CSS layout API will let us literally define
our own display properties. If there’s an awesome new layout we want,
we can polyfill that. And because everyone likes a good masonry
layout, you can actually build one without affecting performance for your end user. Now, there’s a lot of terminology in here,
because this spec literally aims to describe how layout works on the web. That’s that second part of the Houdini task
force mission. So let’s go through some of this terminology. We have a parent layout. That parent layout is the container. That’s the thing that we put, like, a display
grid or display flex box on. Inside that parent container, we have something
called constraint space. That constraint space is the available space
that we can actually put elements inside. So it’s the space available in our element
after border and padding and scroll bars have been removed. Border, padding, scroll bars. Those are all layout edges. Each of the elements inside has a current
layout. That’s the layout being worked on. And the current layout has a child layout,
which is the algorithm for the layout child from the current layout. It’s confusing. They name things poorly. But that’s what it is. I’m sorry. Layout child has styling information of the
element that’s being worked on. But it doesn’t have position layout. Position layout — position information comes
from fragments that are generated from a layout child. If we look at a layout child, a layout child,
like I said, has a typed OM-style map on it. It can be generated from any block element. So block elements… Root inline boxes. So text can generate layout children. It can be generated from before and after
pseudoelements. And it can be generated from blockified elements
that aren’t really blockified. So things that have display table cell but
don’t have a parent display table. They also also include this layout next fragment
function that will generate a fragment from it. That fragment includes inline size and block
size. Inline being in parallel with the writing
text block being in perpendicular. Those we can’t change. Those come from the render engine. But we can set inline offset and block offset. This is what lets us actually position our
elements around. Finally layout edges. I know this was a little bit big. But it has border, padding, scroll bar. The inline start, inline end, and full inline
size of that block size for all three of those items, and it also has all of the edges in
the block and the inline direction. So let’s take a look at the worklet now. The worklet has input properties that we want
to get from the parent. So this is a thing like… Things like display grid or grid column, grid
template columns. And then there are also children properties
that we might want to get. For instance, grid columns, grid rows. We can also decide whether or not all of the
elements we want, all the children we want to be blockified like in flexbox and grid,
or whether we want them to just kind of keep whatever blocking they already have. Now it takes… It has two methods. Both are generators, so they can run in async
and parallel. One is intrinsic size and one is layout. And we’ll kind of dive into these in a minute. So an example of kind of a block layout that
we currently might recognize looks something like this. We have intrinsic size, and this comes from
the intrinsic size spec. And what this will do is this will let us
figure out, determine what the max size of a block is. So how big a block can be. So that all of the contents in there — and
there’s no unused space. And how small it could be, how small we can
make that block without overflowing our content. And we return the min and the max from there. Then we start looking at layout. We’re gonna get the inline size of our element
using the utility function that just figures out the inline size. We’re gonna figure out both the inline and
the block size. We’re going use the inline and the block size
to generate a new constraint space to put our child layouts in. Our child layouts — we’re going to loop over
each one of our children and figure out, generate fragments from the constraint space in our
parent. And then we start laying stuff out. We’re gonna start laying stuff out where our
block size starts, where all of our edges start. And then we basically loop over each one of
our fragments. Put it in position. Add its height to the total block offset. And then center it based on its inline size. Once we’ve done that, once we’ve put everything
in place, add the block ends, the edge ends, I should say, to our offset, and we calculate
the block size of our parent, and return the inline size, the block size, and its fragments,
this puts everything in place. So it looks like this. We have this big black border, which is our
layout. Something that has layout, block layout, as
its display. Its constraint space is inside of it, and
it gets block fragments, one on top of another, centered in that space. And it will eventually look like this. This isn’t an actual layout, because it hasn’t
been shipped anywhere, but what we’ve just done in that worklet will wind up producing
a layout that looks like this, where we have all items centered, one on top of another. We’re almost done. Before we’re done, a couple more super-duper
fuzzy things that are interesting, but we can’t really talk about them in depth. The first one is the CSS parser API, that
will let us take strings that are full of rule sets and convert them into typed CSS
for us. So throw out all your CSS parsers and just
use the browser. The second is the font metrics API, which
will give deep insights into how fonts behave on the web so we can make better choices about
fonts. And the final is the boxtree API, which gives
us APIs that explain how things are laid out from our browser code, as opposed to just
doing it through our layouts. Overall, with Houdini, the future is pretty
bright, and we should really get excited about this type of stuff. Because while you can’t use it today, it’s
coming, and it’s going to enable a whole new set of awesomeness on the web. Imagine how great it is to have grid layout
finally, but for all eternity. Because we can just start writing our own
grid specs. And with this, we’re gonna be able to perform
our own magic tricks on the web, which is kind of awesome. And I’m really looking forward to it. Thank you! (applause)>>Thank you, Sam! That was really, really exciting. First question: Can I use this yet?>>No!!!!!!!!>>So when will CSS Houdini be ready for most
modern browsers?>>That’s a good question. The new animation API and the new layout API,
I was told they’re gonna start working on it to get implementations in Canary, sometime
later this year. We’re probably gonna see typed OM come into
production first, kind of across browsers. But it’s kind of hard to tell. It’s an API-by-API basis.>>Nice. Very exciting. Worklets being basically render hooks — is
there a limitation about rendering time? Are they killed by the browser after predefined
execution time?>>Yes, they are. So if a render, if a worklet takes too long
to render, then the render engine will just skip the frame that the worklet is trying
to paint and then keep going.>>Sweet. Debugging CSS is really annoying. Are there any new strategies or ideas to improve
the dev experience with Houdini?>>So right now, there are no… There is nothing in Houdini that aims specifically
to help with debugging CSS. But you can debug worklets with the same type
of JavaScript tools you already have.>>What are you using for the inline code
editing in your slides?>>So the inline code editing in my slides
is a hack I put together. I’m using prismJS by Lia Verue to do syntax
highlighting, and I have a text area with transparent text sitting on top of it. That when I make changes to the transparent
text, it updates the syntax highlighting behind it. It’s a really nifty hack.>>Very nice. Some folks were curious about the pink picture
in the bottom right of each of your slides.>>So that pink picture is the mini-map of
the whole slide deck. And when something is partially filled in,
it means I still have fragments left on that slide, and when it’s fully filled in, it means
I have no more fragments on that slide.>>Awesome. Thank you so much, Sam! Let’s have another round of applause!