 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 objective of the CSS tag Houdini Task Force is to jointly develop features that explain the magic of styling and layout on the web, you know, the magic of being able to do that. Practically, though, what does that mean? And 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? Post-CSS does this. There are lots of JavaScript libraries 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 in to render engines, and we're going to 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 of 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 Saskonf, 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 particularly like it. And then the other one is Serma, who is a web advocate and engineer at Google. Serma has been answering all of the questions I've had about Houdini over the past couple weeks while I've been updating this talk, I should say, because some of this stuff changes pretty fast. So the first question that everyone is going to ask once you start to see all this cool stuff is, can I use Houdini? And the answer, unequivocally, is no. 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 furthest ahead, canary specifically. There's a lot of interest in typed OM. 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 for the past year, and over the past year, there have been four API incompatible breaking changes. Some of these docs that I'm going to 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 might 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 sublet 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 that we can work with worklets after they've been loaded. And one of the examples I'm going to 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 registered 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. Contagent customer or optional arguments might return stuff, might just work directly on the objects that get passed in. It kind of depends on the worklet. So let's look at this lifecycle. We start with our 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 will go. It will grab our worklet, and then it will 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, aka CSS OM V2, the new hot lists, or big CSS, if you like it that way. From their wiki, converting CSS object model values, or strings, I should say, into meaningfully typed JavaScript representations in 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 CSS unit values, which are either a raw number, a number with a unit, or a raw percentage. Basic numbers, basically. And then there is CSS math value, which is kind of more complex numbers, things like calcs and minns and maxes and sums. And what a CSS math value does is eventually all of 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 29, 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, kind of 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 positions 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 negative 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's 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 snazberries taste like snazberries. 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 reset it to a URL. And 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, hey, look, I have this property. It's called this. Its syntax is this. Now that's definitely a color because we've 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 register property is you have a name that's a string. You have the syntax, which is a couple different options. I'm going to 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 OK. 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 going to have it inherit up the tree. And its initial value we're going to 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 if you see right now, if I hover over this registered button, it transitions. Nice, pretty. But it gets if we change from coffee to red. Red works, and I can transition. But even better, if I copy this URL, and again, URL's not a color. If I go and change registered color to that URL, you'll see it defaults to Rebecca Purple because registered property 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 going to talk about is CSS Paint. 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 import properties, and that will actually look at the elements definition for the properties that you're looking for and bring them in for us to use as typed OM. There are also arguments, so you can optionally include arguments when you pass to the new Paint function. And those are the same type 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-a-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 going to look at this demo. It's a circle Paint Worklet. We're going to draw a circle centered in our box. So we're going to 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 going to do a little bit of drawing. So we're going to do some math to figure out the position of the circle and the radius. And then we're going to start to draw. And if you've ever used canvas drawing, this looks very similar. We're going to begin our path. We're going to 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, add module, 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. But 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. 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. OK. 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 dog, I heard you like parallax. What 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 is a constructor, because we're going to 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 going to 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 going to set the timeline equal to whatever the scroll is. And our second one, we're going to 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 and 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. So maybe this will be on window.css to the end. Maybe it won't be. Who knows. But the key thing here isn't whether or not it's on window.css. The key thing here is that once our worklet is loaded, we instantiate 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. 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 0 to 0.5. And then we want to set its duration. Now, we are actually, as part of the animation API, feeding the animation timeline directly into the keyframe effects. So we don't want any duration. We want a one millisecond duration with no iterations. We basically want this to go frame by frame based on the timeline we pass in. Sorry, that's the avatar. That's 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 up. And then the same duration. The second input that we have for a new animator, a new animation animator, is the scroll timeline. Now, this is something new. They're working on other user inputs like touch and 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 going to 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 0. 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 Sorma's put together. So as we scroll, you'll see that that header starts to fade in and that avatar starts to get small. And as we scroll, that's opacity in, header small, and it will 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 in its own isolated space, which is exactly what we want for this type of effect. The fourth and final work that we're going to 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 divs, with our layouts. The CSS Layout API will let us literally define our own display properties. If there is 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 the 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 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 boarding or border and padding and scroll bars have been removed. Border padding scroll bars, those are our 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 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 pseudo elements. 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 all also include this layoutNextFragment 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 is a little bit big, but it has border, padding, scroll bar, the inline start, inline end, and full inline size as well as block start, block end, 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 things like display grid or grid column or 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 the elements we want are all the children who 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 might be, which is how small we can make that block without starting overflowing our content. And we return them in in the max from there. We then start to look at layout. So the first thing we're going to do is we're going to get the inline size of our element using a utility function that just figures out the inline size. We're going to figure out both the inline and the block size. We're going to use that inline and 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 or generate fragments from the constraint space in our parent. And then we start to laying stuff out. We're going to 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 actually put everything in place, then we 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 a layout block layout as its display. Its constraint space is the area inside of it. And then each one of those fragments gets put one right on top of another centered in that constraint 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 work 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 in depth. The first one is the CSS parser API, which is going to let us take strings that either look like CSS rules or full rule sets and convert it into typed OM for us. So throw out all of your string-based CSS parsers and just use the browser. The next one is the Font Metrics API, which is going to give us lots of deep insights into how fonts behave on the web that we can then use to make better choices about fonts. And then the final one is the BoxTree API, which aims to actually give 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 going to 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. 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 going to start working on it to get implementations in Canary sometime later this year. We're probably going to 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 a predefined execution time? Yes, they are. So if a worklet takes too long to render, then the render engine will just skip the frame that the worklet's 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 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 Prism.js by Leah Varue 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, that means I have no more fragments on that slide. Awesome. Thank you so much, Sam. Let's give him another round of applause.