Skip to content

Latest commit

 

History

History
 
 

ramp

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

@thi.ng/ramp

npm version npm downloads Mastodon Follow

Note

This is one of 199 standalone projects, maintained as part of the @thi.ng/umbrella monorepo and anti-framework.

🚀 Please help me to work full-time on these projects by sponsoring me on GitHub. Thank you! ❤️

About

Extensible keyframe interpolation/tweening of arbitrary, nested types.

screenshot

This package can perform keyframe interpolation for ramps of numeric values, n-dimensional vectors and nested objects of the same. It provides several interpolation methods out of the box, but can be extended by implementing a simple interface to achieve other interpolation methods.

Built-in interpolation modes:

Status

STABLE - used in production

Search or submit any issues for this package

Installation

yarn add @thi.ng/ramp

ESM import:

import * as ramp from "@thi.ng/ramp";

Browser ESM import:

<script type="module" src="https://esm.run/@thi.ng/ramp"></script>

JSDelivr documentation

For Node.js REPL:

const ramp = await import("@thi.ng/ramp");

Package sizes (brotli'd, pre-treeshake): ESM: 2.00 KB

Dependencies

Note: @thi.ng/api is in most cases a type-only import (not used at runtime)

Usage examples

Two projects in this repo's /examples directory are using this package:

Screenshot Description Live demo Source
Scroll-based, reactive, multi-param CSS animation basics Demo Source
Unison wavetable synth with waveform editor Demo Source

API

Generated API docs

Numeric

import { linear, hermite, easing } from "@thi.ng/ramp";

const stops = [[0.1, 0], [0.5, 1], [0.9, 0]];

const rampL = linear(stops);
const rampH = hermite(stops);
const rampE = easing(stops);

for(let i = 0; i <= 10; i++) {
    const t = i / 10;
    console.log(t, rampL.at(t).toFixed(3), rampH.at(t).toFixed(3), rampE.at(t).toFixed(3));
}

// 0   0.000 0.000 0.000
// 0.1 0.000 0.000 0.000
// 0.2 0.250 0.156 0.016
// 0.3 0.500 0.500 0.500
// 0.4 0.750 0.844 0.984
// 0.5 1.000 1.000 1.000
// 0.6 0.750 0.844 0.984
// 0.7 0.500 0.500 0.500
// 0.8 0.250 0.156 0.016
// 0.9 0.000 0.000 0.000
// 1   0.000 0.000 0.000

nD vectors

import { HERMITE_V, VEC3, ramp } from "@thi.ng/ramp";
import { FORMATTER } from "@thi.ng/vectors";

// use the generic `ramp()` factory function with a custom implementation
// see: https://docs.thi.ng/umbrella/ramp/interfaces/RampImpl.html
const rgb = ramp(
    // use a vector interpolation preset with the VEC3 API
    HERMITE_V(VEC3),
    // keyframes
    [
        [0.0, [1, 0, 0]], // red
        [0.5, [0, 1, 0]], // green
        [1.0, [0, 0, 1]], // blue
    ]
);

for (let i = 0; i <= 20; i++) {
    const t = i / 20;
    console.log(t, FORMATTER(rgb.at(t)));
}

// 0    [1.000, 0.000, 0.000]
// 0.05 [0.972, 0.028, 0.000]
// 0.1  [0.896, 0.104, 0.000]
// 0.15 [0.784, 0.216, 0.000]
// 0.2  [0.648, 0.352, 0.000]
// 0.25 [0.500, 0.500, 0.000]
// 0.3  [0.352, 0.648, 0.000]
// 0.35 [0.216, 0.784, 0.000]
// 0.4  [0.104, 0.896, 0.000]
// 0.45 [0.028, 0.972, 0.000]
// 0.5  [0.000, 1.000, 0.000]
// 0.55 [0.000, 0.972, 0.028]
// 0.6  [0.000, 0.896, 0.104]
// 0.65 [0.000, 0.784, 0.216]
// 0.7  [0.000, 0.648, 0.352]
// 0.75 [0.000, 0.500, 0.500]
// 0.8  [0.000, 0.352, 0.648]
// 0.85 [0.000, 0.216, 0.784]
// 0.9  [0.000, 0.104, 0.896]
// 0.95 [0.000, 0.028, 0.972]
// 1    [0.000, 0.000, 1.000]

Nested objects

import { HERMITE_V, LINEAR_N, VEC2, nested, ramp } from "@thi.ng/ramp";

const r = ramp(
    nested({
        a: LINEAR_N,
        b: nested({
            c: HERMITE_V(VEC2),
        }),
    }),
    [
        [0, { a: 0, b: { c: [100, 100] } }],
        [0.5, { a: 10, b: { c: [300, 50] } }],
        [1, { a: -10, b: { c: [200, 120] } }],
    ]
);

// produce an iterator of N uniformly spaced sample points
// across the full range of the ramp
console.log([...r.samples(10)]);

// [
// 	[0, { a: 0, b: { c: [100, 100] } }],
// 	[0.1, { a: 2, b: { c: [120.8, 94.8] } }],
// 	[0.2, { a: 4, b: { c: [170.4, 82.4] } }],
// 	[0.3, { a: 6, b: { c: [229.6, 67.6] } }],
// 	[0.4, { a: 8, b: { c: [279.2, 55.2] } }],
// 	[0.5, { a: 10, b: { c: [300, 50] } }],
// 	[0.6, { a: 6, b: { c: [289.6, 57.28] } }],
// 	[0.7, { a: 2, b: { c: [264.8, 74.64] } }],
// 	[0.8, { a: -2, b: { c: [235.2, 95.36] } }],
// 	[0.9, { a: -6, b: { c: [210.4, 112.72] } }],
// 	[1, { a: -10, b: { c: [200, 120] } }]
// ]

Grouped & nested ramps

import { group, linear, wrap } from "@thi.ng/ramp";

const example = group({
    // child timeline with looping behavior
    a: linear([[0, 0], [20, 100]], { domain: wrap }),
    // another child timeline
    b: linear([[10, 100], [90, 200]]),
});

console.log(JSON.stringify([...example.samples(10, 0, 100)]));
// [
// 	[0, { a: 0, b: 100 }],
// 	[10, { a: 50, b: 100 }],
// 	[20, { a: 100, b: 112.5 }],
// 	[30, { a: 50, b: 125 }],
// 	[40, { a: 100, b: 137.5 }],
// 	[50, { a: 50, b: 150 }],
// 	[60, { a: 0, b: 162.5 }],
// 	[70, { a: 50, b: 175 }],
// 	[80, { a: 0, b: 187.5 }],
// 	[90, { a: 50, b: 200 }],
// 	[100, { a: 50, b: 200 }]
// ]

Authors

If this project contributes to an academic publication, please cite it as:

@misc{thing-ramp,
  title = "@thi.ng/ramp",
  author = "Karsten Schmidt",
  note = "https://thi.ng/ramp",
  year = 2019
}

License

© 2019 - 2024 Karsten Schmidt // Apache License 2.0