Skip to content

Instantly share code, notes, and snippets.

What would you like to do?

Properties of Custom Paint

We wanted to capture a solid notion of the required and desirable properties of a Custom Paint API, so we can agree on goals before descending into API or implementation details.

So here’s our list :) Anything we should change, remove, or add?

Required Properties of Custom Paint

Custom Paint code happens explicitly after layout

We want custom paint to be able to respond to layout changes, otherwise it's not really much different to a canvas.

Custom Paint code can’t cause another style-layout-paint to occur before pixels on screen.

We don’t want custom paint code to be able to invalidate style/layout/paint within the same frame. We’ve seen this on the main thread code with style/layout, and this is a bad thing™. A bad actor can force every frame to do twice the amount of work required, it leads to unbounded amounts of work.

Custom Paint code is invalidated, not drawn on every frame (if it doesn’t need it).

We want to end up in a situation where we can intelligently not invoke a callback because it is not needed this frame. For example if the layout & style of an element hasn’t changed, we explicitly want to not call the callback, and just use a cached value.

Paint regions are invalidated by style.

We need a system that interoperates well with the built-ins and that explains CSS. That makes it so that web authors don’t need to understand two different systems. Part of this is that style invalidations should trigger an invalidation of paint on an element. It shouldn’t skip the style/layout invalidation system.

Custom paint augments existing elements in the DOM.

We don’t want web authors to rebuild the world in canvas if they don’t want to. If they just want to make their button have a new type of custom border, background, box-shadow, etc, etc, etc, they should be able to modify the paint on an existing element.

We don’t want Web Developers to increase DOM count/complexity, or muck around with layouts to achieve these effects.

Predictable behaviour.

The Web Developer should know how their paint code should behave. There shouldn’t be hidden magic.

Geometry Data passed in is correct and valuable.

Geom data passed in shouldn’t change underneath the system. Have geometry data of children available as well (may be useful for grid effects, etc).

Custom Paint functions can be shared across multiple elements.

We'd don't want a canvas to be shared across multiple elements. This means that it won't be able to properly respond to geometry sizes of the element it is "augmenting"; it'll have to stretch, crop, etc. Instead we'd like to share a Custom Paint function instead, which can properly respond to geometry changes, and be invoked multiple times.

Nice to have properties of Custom Paint

Fast RenderingContext (no foot-guns).

We shouldn’t have to maintain a full backing store for each canvas by default, should be disabled. This can be achieved by flipping the ‘tainted’ bit on a rendering context. This disallows pixel data readback.

Not explicitly tied to a particular thread / number of threads in the browser.

We’d like custom paint to not have thread affinity.

Consistent with other Houdini APIs, e.g. Custom Box Layout, Line Layout, Custom Style Resolution.

We’d like for all the APIs within this group be sane and consistent with each other.

Can define “overflow” on an element to achieve things like box-shadow.

As described. :)

Properties of Custom Paint that are up for debate

Able to optionally access written pixels

Should it be possible to flip a custom paint handler into a mode where pixels written by the JS function are able to be read back? Given ‘fast by default’, this wouldn’t normally be on.

Able to mark handlers as optional

There are some concerns about custom paint handlers causing performance issues. Should developers be able to mark custom painters as optional, so that when under load the browser can decide not to run them?


This comment has been minimized.

Copy link

shans commented May 19, 2015

Part of the Geometry data point is up for debate: should custom paint handlers have access to information about child position?


This comment has been minimized.

Copy link

jonrimmer commented May 20, 2015

Before getting into the weeds of designing custom paint APIs, wouldn't a simpler first step to be for browsers to add a layout invalidation event to the canvas element?

As it stands, we have some pretty good existing mechanisms – canvas and SVG elements – for doing custom painting, but no way to properly integrate them into layout. This seems like such an easy use-case to address that I can't understand why it's been left undone.


This comment has been minimized.

Copy link

slightlyoff commented Jun 1, 2015

I like all of this. A few questions:

  • Firefox, at one point, had paint counters/callbacks. I wonder what they learned from them?
  • Style invalidation seems good...but what about partial invalidation? How/does that work?
  • What happens on pages without style, per sae? Can they still draw per frame, e.g. in response to mouse events? How?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.