Point2DSpring is composed of an independent spring on each axis. It handles the boilerplate of deconstructing and recombining points into numbers, and feeding those values into the underlying springs.
Because it is composed of independent springs, it will dispatch when either changes. If both are changing, this could be twice per frame. It should probably be _debounced, but that adds complexity to state$ (because would need to wait until the next frame before setting itself to rest). For now, I'm punting on that complexity. In practice, an overly chatty spring shouldn't matter that much. If it doesn, authors can always call _debounce() themselves until I get around to adding that coordination in Point2DSpring.