Conversation
…anges The ConstantTime* distortions apply a flat rate across the whole window, so the clock's speed snaps open at window start, closed at window end, and at every hand-off in between -- the exact tell the bedside-clock use case is trying to hide. Add an EasedTimeDistortion family whose rate varies smoothly across the window: a raised-cosine bump that eases up from normal speed, peaks at the midpoint, and eases back out, with no kink at the seams. EasedTimeDilation and EasedTimeCompression are drop-in replacements for the constant pair, taking the same three arguments. distortTime returns the definite integral of the rate over [offset, offset + length] -- the difference of a normalized cumulative at the slice endpoints -- not a point sample, so the offset Clock accumulates stays independent of polling frequency. cumulativeFraction(0)=0 and cumulativeFraction(1)=1 keep the whole-window contract intact: over referenceDuration of real time the fake clock still advances exactly relativeDuration, so composed windows net out identically to the constant case. Custom curves (Gaussian, smoothstep) subclass EasedTimeDistortion and override cumulativeFraction. Closes #9 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…te changes
The ConstantTime* distortions apply a flat rate across the whole window, so the
clock's speed snaps open at window start, closed at window end, and at every
hand-off in between -- the exact tell the bedside-clock use case is trying to
hide.
Add an EasedTimeDistortion family whose rate varies smoothly across the window:
a trapezoid with cosine-eased corners. By default the two ramps meet at the
midpoint (a raised-cosine bump), so the rate eases up from normal speed, peaks,
and eases back out with no kink at the seams. EasedTimeDilation and
EasedTimeCompression are drop-in replacements for the constant pair, taking the
same three arguments.
An optional fourth argument shapes the easing. { ramp } sets both ease regions;
{ rampIn, rampOut } override each side and win over the shorthand, CSS-padding
style (a side of 0 is a hard seam). Narrowing the ramps opens a flat plateau in
the middle, decoupling the peak deviation from the window average so the
slowdown can be sustained and gentle, or asymmetric. Each ramp accepts a
Duration of real time (the natural unit for imperceptibility, an absolute-time
effect) or a bare number as a fraction of the window (handy for a normalized
control); overlapping ramps are clamped, not rejected.
distortTime returns the definite integral of the rate over
[offset, offset + length] -- the difference of a normalized cumulative at the
slice endpoints -- not a point sample, so the offset Clock accumulates stays
independent of polling frequency. The cumulative runs 0 -> 1 for any ramp, so
the whole-window contract is unchanged: over referenceDuration of real time the
fake clock still advances exactly relativeDuration, and composed windows net out
identically to the constant case. Custom curves subclass EasedTimeDistortion and
override cumulativeFraction.
Closes #9
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #9.
The
ConstantTime*distortions are piecewise-linear, so the rate snaps at every window boundary — the visible tell the "ease into the slowing" use case wants to hide. This adds a distortion family whose rate varies smoothly across the window.What's new
EasedTimeDistortion— base class extendingRelativeTimeDistortion. The rate follows a trapezoid with cosine-eased corners: a rising half-cosine ramp, a flat plateau, then a falling ramp.distortTimereturns the difference of a normalized cumulativecumulativeFraction(u)at the slice endpoints.EasedTimeDilation/EasedTimeCompression— semantic subclasses mirroring theConstant*pair; same three arguments, drop-in swap.Shaping the easing
Optional fourth argument
{ ramp, rampIn, rampOut }:rampsets both ease regions;rampIn/rampOutoverride per side and win over the shorthand, CSS-padding style. A side of0is a hard, constant-rate seam.Durationof real time (the natural unit for imperceptibility, which is an absolute-time effect) or a bare number as a fraction of the window (handy when driven by a normalized control like a slider).ramp = 0.5→ the symmetric raised-cosine bump, where the ramps meet at the midpoint and peak deviation is twice the average. Narrowing the ramps opens a plateau, decoupling peak from average so the slowdown can be sustained and gentle, or asymmetric (long onset, quick roll-off). Overlapping ramps are clamped proportionally, not rejected.The endpoints are limiting cases of
ramp:0.5is the bump,0collapses to the hard-seamConstantTime*behavior.Why not Gaussian
A Gaussian bump is the same single-hump character as the cosine but costs an
erfapproximation, and a truncated Gaussian doesn't reach zero at the seams without a pedestal correction. It was dropped: the plateau fills the genuinely different "sustained gentle" need, and the cosine ramp is already C¹ at the boundaries.Invariants (both called out in the issue)
distortTimeis the slice integral, never a point sample, so summing arbitrary uneven slices equals the whole-window value. Tested at the distortion level and at theClocklevel (coarse single poll vs per-minute), including a non-default plateau ramp.cumulativeFractionruns0 → 1for any ramp, so the window still nets exactlyrelative - reference; the README round-trip returns to perfect sync by 10am, same as the constant version. A dedicated test confirms ~1:1 speed at every seam while the distortion is deepest mid-window.The eased path keeps the constant distortion's exact endpoints — both fall 3h behind by 7am and return to zero by 10am — but meets every seam with a flat tangent instead of a corner:
Extensibility
Custom curves subclass
EasedTimeDistortionand overridecumulativeFraction— the base integrates it per slice. README shows a smoothstep example.Tests / checks
All 87 tests pass under the CI config; 100% coverage on the new file; eslint and
tsc --noEmitclean.🤖 Generated with Claude Code