baked.design

How to put a web page under water

The Wishing Pond and Snowfall are real web pages you can type into while they ripple and freeze. Here's how they work, one small step at a time. Every demo below is live.

Checking your browser…

Put the page inside a canvas

Normally a canvas only draws shapes and pictures. Chrome's new HTML-in-Canvas lets you put real HTML inside one and paint it yourself. The browser still lays it out, so it keeps working like normal HTML, but nothing shows until you draw it.

Type in the field. The reflection types too, because it's the same pixels drawn twice.

<canvas id="stage" layoutsubtree content="drawable">
  <form id="card" drawable>
    <input placeholder="Your name">
    <button>Say hi</button>
  </form>
</canvas>

<script>
  const ctx = stage.getContext("2d");
  stage.onpaint = () => {
    ctx.reset();
    ctx.drawElementImage(card, 0, 0);
  };
</script>

Bend the pixels

Once the page is pixels, you can hand them to WebGL and do anything a shader can do. Here every pixel is read from a slightly wavy spot. CSS can move and tilt a page, but it can't do this.

60%
// copy the 2D canvas into a WebGL texture after each paint
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
  gl.UNSIGNED_BYTE, stage);

// fragment shader: read each pixel from a wavy spot
vec2 q = uv;
q.x += sin(uv.y * 18. + time) * .012 * wobble;
q.y += sin(uv.x * 13. + time) * .01 * wobble;
gl_FragColor = texture2D(page, q);

Make it react

The wobble gets interesting when it comes from you. Under the card is a grid of water heights. Moving your pointer drops pebbles, the waves spread, and the slope of the water bends the page. That's the whole Wishing Pond.

Drag across the water, then toss a coin. The button still works under the water.

// a grid of water heights; your pointer drops pebbles
heights[i] += 40;

// each frame, every cell becomes the average of its
// neighbours minus where it was: waves spread and fade
next[i] = (left + right + up + down) / 2 - prev[i];
next[i] *= 0.975;

// in the shader, the slope of the water bends the page
vec2 slope = vec2(right - left, up - down);
gl_FragColor = texture2D(page, uv + slope * .18);

Ship it to everyone

HTML-in-Canvas is still an experiment, so normal Chrome only turns it on for sites that ask. Asking is free and takes two minutes.

  1. Register your sitePick HTML-in-Canvas on Chrome Origin Trials and enter your domain. You get a token.2 min
  2. Add the tokenOne meta tag in the page head, or an Origin-Trial header.1 min
  3. Check before you drawTest for drawElementImage and show the plain page where it's missing.5 min
  4. Test it for realOpen it in normal desktop Chrome. Safari, Firefox and phones get the plain page.1 min
<meta http-equiv="origin-trial" content="YOUR_TOKEN">

<script>
  const works = "drawElementImage" in CanvasRenderingContext2D.prototype;
  if (!works) {
    // Safari, Firefox and phones: show the page as it is
  }
</script>

Questions

Can people still click and type?

Yes. The real HTML is still there, and the browser maps clicks from the drawing back to it. Small wobbles are fine. Big ones can make a click land a little off.

Does it work on iPhone?

Not yet. It's Chrome on desktop for now. Always keep a plain version, and record a video for social, since most people scroll on phones.

Is it slow?

The page is only redrawn when it changes. The water runs on the graphics card, so it's light. Keep ripple grids small, around 200 cells across.

Is it accessible?

Screen readers and keyboards use the real HTML, so they work as normal. Respect reduced motion: calm the effect down or switch it off.

When does the trial end?

March 1, 2027. Chrome may ship it for everyone before then, or change it. Build so the plain page always works.

See the full ones

Wishing Pond: a form at the bottom of a koi pond. Type a wish, toss a coin, watch your words sink.

Snowfall: a form standing in the snow. Snow settles on its fields and letters, and you can brush it off.

Margin Dragon: a watercolour dragon swims through a page of real text, and its fire burns holes in it.

More things on Fun