Demonstration · in-place editing

This page is ordinary on purpose.

There is an object drifting behind this column. It is not part of the layout, it is not in a hero, and nothing here was built around it. That is the point: it was placed on top of a page that already existed, which is how anyone would actually use this.

The editor is live on this page. Nothing to install, and nothing you do here can break anything — it saves to your own browser.

  1. 01

    Open it

    Press ⌘⇧E, or click the lf button in the bottom corner. The page stays exactly as it is — the editor is an overlay, not a different screen, because the whole point is judging the object against the real thing.

  2. 02

    Put it somewhere

    In Place, drag the object. Pick a shape and one of the hundred and eight colourways from the Element row while you are there — what it is and where it goes are the same decision, so they sit in the same bar.

  3. 03

    Draw where it goes as you scroll

    Switch to Path and drag out a route. That line is what the object follows as this page scrolls. Drag any handle to adjust it, alt-click a handle to delete one, and pull Scrub to preview the whole journey without scrolling the page under yourself.

  4. 04

    Size it along the way

    In Size, click a handle and set how big the object is at that point. It can swell as it passes behind the text and shrink again on the way out.

  5. 05

    Change what it is on the way

    Back in Path, select a handle and pick a different shape or look in the Element row. That point becomes a checkpoint, drawn as a diamond, and the object melts into the new element as the page scrolls past it. Two are set already — scroll and the knot becomes a word, then a capsule.

  6. 06

    Tie it to the page, then save

    In Pin, click a heading: that point is now reached when the heading is at that height, so editing the copy above it cannot knock the timing out. Route around content draws a path through the empty space for you. Then press Save and look at the file at the bottom of this page — that JSON is everything.

Why it can be deleted

The editor holds no state of its own. It reads a placement, mutates a draft, and writes it back. So removing it from your project is one deleted line — the object keeps its position, keeps its path, keeps its sizes, because none of that ever lived in the editor. Put the line back a month later and it opens on exactly what is on screen, since that is the only thing it has ever read.

You can watch that happen here. Save something, then reload the page. The editor starts closed and the object is still where you put it, because it was never the editor holding it there.

Scroll on

Keep going and watch the object take its route. The motion is driven by how far down this page you are, mapped to distance along the path rather than to the number of points in it — so it moves evenly even where the drawn line bunches up.

A placement is about two hundred bytes of JSON: a handful of points, each a pair of fractions and sometimes a size. That smallness is not incidental — it is what makes the file reviewable, what makes it merge cleanly, and what makes it reasonable to keep one per page rather than one per project.

Positions are stored as fractions of the frame rather than pixels, so a placement made on a wide monitor still means the same thing on a phone. Sizes are fractions of width specifically, because pages get taller and not wider; tying size to height would make every object shrink on a laptop.

The lag control is the one parameter that is purely about feel. At one, the object is welded to the scrollbar and moves in perfect lockstep, which reads as mechanical. Lower it and the object chases its scroll position instead of occupying it, arriving a moment late and overshooting slightly — which is what makes it feel like an object rather than a value.

None of this needs a scroll library. The position is a transform written straight to the node inside an animation frame, and the component above it never re-renders once it has mounted.

End of the page · the object has finished its path

The file, as it stands

Open the editor, move the object, and this updates. It is about two hundred bytes: a few points, each a pair of fractions and sometimes a size.

{
  "drift": {
    "origin": {
      "x": 0.76,
      "y": 0.28,
      "size": 0.3
    },
    "frame": "viewport",
    "layer": 0,
    "path": {
      "points": [
        {
          "x": 0.76,
          "y": 0.28,
          "size": 0.3
        },
        {
          "x": 0.26,
          "y": 0.48,
          "size": 0.42,
          "object": {
            "type": "text",
            "value": "MELT",
            "depth": 0.45,
            "bevel": 0.03
          },
          "preset": "aurora-3"
        },
        {
          "x": 0.72,
          "y": 0.66,
          "size": 0.24,
          "object": {
            "type": "shape",
            "shape": "capsule",
            "detail": 160
          },
          "preset": "magma-4"
        },
        {
          "x": 0.22,
          "y": 0.34,
          "size": 0.36
        }
      ],
      "ease": 0.09
    },
    "breakpoints": {
      "phone": {
        "origin": {
          "x": 0.82,
          "y": 0.14,
          "size": 0.3
        },
        "path": {
          "points": [
            {
              "x": 0.82,
              "y": 0.14,
              "size": 0.3
            },
            {
              "x": 0.8,
              "y": 0.62,
              "size": 0.34,
              "object": {
                "type": "text",
                "value": "MELT",
                "depth": 0.45,
                "bevel": 0.03
              },
              "preset": "aurora-3"
            },
            {
              "x": 0.2,
              "y": 0.82,
              "size": 0.24,
              "object": {
                "type": "shape",
                "shape": "capsule",
                "detail": 160
              },
              "preset": "magma-4"
            },
            {
              "x": 0.8,
              "y": 0.2,
              "size": 0.3
            }
          ],
          "ease": 0.09
        }
      }
    }
  }
}

On this page saving writes to your browser, because a hosted demo has no repository. In your own project the same button writes that file to disk, and the dev route that does it is four lines.