UNPKG

osc-css

Version:

CSS properties to animate numeric values where they are needed using oscillators and envelopes.

126 lines (100 loc) 6.38 kB
/** ; v0.8.0 ; LICENSE: MIT Copyright (c) 2025 Nils Riedemann ___ ____ ___ ___ ____ ____ / __`\ /',__\ /'___\ /'___\ /',__\ /',__\ /\ \L\ \/\__, `\/\ \__/ __/\ \__//\__, `\/\__, `\ \ \____/\/\____/\ \____\/\_\ \____\/\____/\/\____/ \/___/ \/___/ \/____/\/_/\/____/\/___/ \/___/ CSS properties to animate numeric values where they are needed using oscillators and envelopes. https://osc.style/ */ :where(*) { --_osc-time-unit: 1s; --_osc-state: var(--osc-state, running); --_osc-env-iterations: var(--osc-env-iterations, infinite); --_osc-global-frequency: var(--osc-frequency, 1); --_osc-sin-frequency: var(--osc-sin-frequency, var(--_osc-global-frequency)); --_osc-cos-frequency: var(--osc-cos-frequency, var(--_osc-global-frequency)); --_osc-tri-frequency: var(--osc-tri-frequency, var(--_osc-global-frequency)); --_osc-saw-frequency: var(--osc-saw-frequency, var(--_osc-global-frequency)); --_osc-global-phase: var(--osc-phase, 0s); --_osc-sin-phase: calc(var(--_osc-global-phase) + var(--osc-sin-phase, 0s)); --_osc-cos-phase: calc(var(--_osc-global-phase) + var(--osc-cos-phase, 0s)); --_osc-saw-phase: calc(var(--_osc-global-phase) + var(--osc-saw-phase, 0s)); --_osc-tri-phase: calc(var(--_osc-global-phase) + var(--osc-tri-phase, 0s)); /* TODO: feat: add iteration-delay (post envelope delay, poc: https://codepen.io/nocksock/pen/rNEPMjJ) */ --_osc-env-delay: var(--env-delay, 0); --_osc-env-attack: var(--env-attack, 0); --_osc-env-hold: var(--env-hold, 0); --_osc-env-decay: var(--env-decay, 1); --_osc-env-total: calc(var(--_osc-env-delay) + var(--_osc-env-attack) + var(--_osc-env-hold) + var(--_osc-env-decay)); --_osc-delay-start: calc(var(--_osc-env-delay) / var(--_osc-env-total) * 100%); --_osc-attack-start: calc((var(--_osc-env-delay) + var(--_osc-env-attack)) / var(--_osc-env-total) * 100%); --_osc-hold-start: calc((var(--_osc-env-attack) + var(--_osc-env-hold)) / var(--_osc-env-total) * 100%); --_osc-decay-start: calc(var(--_osc-hold-start)); --env-dahd: linear(0 0%, 0 var(--_osc-delay-start), 1 var(--_osc-attack-start), 1 var(--_osc-hold-start), 1 var(--_osc-decay-start), 0); --_osc-envelope: var(--env, var(--env-dahd)); /* TODO: add pulse wave (maybe w/ slope parameter?) */ --osc-only-sin: calc(1s / var(--_osc-sin-frequency)) linear var(--_osc-sin-phase) infinite osc-sin var(--_osc-state); --osc-only-cos: calc(1s / var(--_osc-cos-frequency)) linear var(--_osc-cos-phase) infinite osc-cos var(--_osc-state); --osc-only-saw: calc(1s / var(--_osc-saw-frequency)) linear var(--_osc-saw-phase) infinite osc-saw var(--_osc-state); --osc-only-tri: calc(1s / var(--_osc-tri-frequency)) linear var(--_osc-tri-phase) alternate infinite osc-tri var(--_osc-state); --osc-only-envelope: calc(var(--_osc-env-total) * var(--_osc-time-unit)) var(--_osc-envelope) var(--_osc-global-phase) forwards var(--_osc-env-iterations) env-amp var(--_osc-state); --osc: var(--osc-only-envelope), var(--osc-only-sin), var(--osc-only-cos), var(--osc-only-saw), var(--osc-only-tri); --osc-sin-y:sin(var(--osc-_sin) * pi); --osc-sin-abs: max(var(--osc-sin-y), -1 * var(--osc-sin-y)); --osc-sin-post-gain: pow(var(--osc-sin-abs), var(--amp-gain)) * var(--osc-sin-y); --osc-sin: calc(var(--osc-sin-post-gain) * var(--amp-volume)); --osc-cos-y:cos(var(--osc-_cos) * pi); --osc-cos-abs: max(var(--osc-cos-y), -1 * var(--osc-cos-y)); --osc-cos-post-gain: pow(var(--osc-cos-abs), var(--amp-gain)) * var(--osc-cos-y); --osc-cos: calc(var(--osc-cos-post-gain) * var(--amp-volume)); --osc-tri-y: var(--osc-_tri); --osc-tri-abs: max(var(--osc-tri-y), -1 * var(--osc-tri-y)); --osc-tri-post-gain: pow(var(--osc-tri-abs), var(--amp-gain)) * var(--osc-tri-y); --osc-tri: calc(var(--osc-tri-post-gain) * var(--amp-volume)); --osc-saw-y: var(--osc-_saw); --osc-saw-abs: max(var(--osc-saw-y), -1 * var(--osc-saw-y)); --osc-saw-post-gain: pow(var(--osc-saw-abs), var(--amp-gain)) * var(--osc-saw-y); --osc-saw: calc(var(--osc-saw-post-gain) * var(--amp-volume)); /* - normalised oscillators - */ --osc-SIN: calc((1 + var(--osc-sin)) / 2); --osc-COS: calc((1 + var(--osc-cos)) / 2); --osc-SAW: calc((1 + var(--osc-saw)) / 2); --osc-TRI: calc((1 + var(--osc-tri)) / 2); } /* - wave forms - */ /* TODO: reconsider implications of inherits true vs false */ @property --osc-_sin { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } @property --osc-_cos { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } @property --osc-_tri { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } @property --osc-_saw { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } @keyframes osc-sin { from { --osc-_sin: -1; } to { --osc-_sin: 1; } } @keyframes osc-cos { from { --osc-_cos: -1; } to { --osc-_cos: 1; } } @keyframes osc-tri { from { --osc-_tri: -1; } to { --osc-_tri: 1; } } @keyframes osc-saw { from { --osc-_saw: -1; } to { --osc-_saw: 1; } } /* - amp envelope - */ @property --env-amp { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } @keyframes env-amp { from { --env-amp: 0; } to { --env-amp: 1; } } @property --_osc-env-total { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } @property --_osc-env-delay { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } @property --_osc-env-attack { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } @property --_osc-env-hold { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } @property --_osc-env-decay { syntax: "<number>" ; initial-value: 1 ; inherits: false ; } @property --_osc-delay-start { syntax: "<percent>" ; initial-value: 0 ; inherits: false ; } @property --_osc-attack-start { syntax: "<percent>" ; initial-value: 0 ; inherits: false ; } @property --_osc-hold-start { syntax: "<percent>" ; initial-value: 0 ; inherits: false ; } @property --_osc-decay-start { syntax: "<percent>" ; initial-value: 0 ; inherits: false ; } @property --amp-volume { syntax: "<number>" ; initial-value: 1 ; inherits: false ; } @property --amp-gain { syntax: "<number>" ; initial-value: 0 ; inherits: false ; } /* TODO: declare any missing properties (also makes debugging easier) */