`animationFPS` and `animationFrames` are only read-only when the node is bound to a fixed-length, fixed-rate frame-based animation. The driver should block sets in that case. The behavior doesn't have a reason to do it.

`animationFPS` can be a simple property that relates `animationFrames` to `animationDuration` like this (paraphrased):

    animationDuration:
      value: 1
    animationFPS:
      value: 30
    animationFrames:
      set: this.animationDuration = value / this.animationFPS;
      get: this.animationDuration * this.animationFPS;

When the driver implements `animationDuration` or `animationFPS` for a node with an attached animation, the value from the driver will supersede the one inherited from the behavior. If the driver also blocks writes, components can't change those properties on the node.

Since `animationFrames` is implemented in terms of the others, the driver will control it indirectly as well. (Although if `animationFPS` comes from the driver's internal duration and frame count, this is a roundabout way to get it.)

When the driver isn't implementing these properties (because it hasn't bound to the node, or the node doesn't have an animation), the behavior's defaults will apply. Frame-based properties will still work even though the animation isn't defined in terms of discrete frames.