The asin() trigonometric function takes a number and returns its inverse for the sin() function. Specifically, it takes a number between -1 and 1 and returns the angle between -90° and 90° (in radians) that, when passed to sin(), produces that number.
.element {
--asin: calc(asin(var(--i) / var(--count)));
/* asin() returns an angle, so to graph it, I used it as a number */
transform: translateY(calc(-1.5px * var(--int-asin)));
}
The asin() function is defined in the CSS Values and Units Module Level 4 specification.
Syntax
asin( <calc-sum> )
The asin() function takes in any calculation that must resolve to a number between -1 and 1, otherwise it returns as not a number, NaN, making the declaration invalid.
Arguments
/* it only takes numbers between -1 and 1 */
rotate: asin(0.3);
rotate: asin(-0.5);
/* it undoes sin() from -90deg to 90deg */
rotate: asin(sin(30deg)); /* equal to 30deg */
rotate: asin(sin(-45deg)); /* equal to -45deg */
/* we can do inner calculations */
rotate: asin(sqrt(2) / 2);
rotate: asin(sqrt(3) / 2);
As you can see, asin() takes a sine’s result between -1 and 1, and undoes it to get the original angle between -90° and 90° in radians.
What’s asin()?
In case you haven’t checked the entry for sin(), which I strongly recommend you do, then you gotta know that it takes an angle and returns the sine for that angle, which is a number between -1 and 1. To be more precise, if we are in a unit circle (a circle of radius one), then we can go around it by an angle, measured from the positive x-axis.
So, given that angle, sin() returns the y-coordinate in the unit circle:
While sin() takes an angle and returns a number between -1 and 1, asin() does the opposite: it takes a number between -1 and 1 and returns the angle used in sin(). Mathematically, this translates to…
y = asin(x); x = sin(y)
You’ll notice from the last demo that sin() goes through three important values:
0at0°and180°1at90°-1at270°
This means we can get 0 in two different ways. In fact, if we keep spinning around the unit circle, all values repeat infinitely. This brings up a conflict: which value should asin(0) return? 0° or 180°? To avoid this kind of inconsistency, asin() output is capped between -90deg and 90deg. As you can see from this graph:

Basic usage
Before starting, let’s get a feel of how asin() behaves. If we have a --progress variable that goes from -1 to 1:
:root {
--progress: 0.5;
}
Then using asin() we could map each value to an angle between -90deg and 90deg. For example, we could rotate an element by an angle given by asin() depending on the current progress.
.element {
rotate: asin(var(--progress));
}
However, if we use asin() as it is, then we are only able to rotate elements between -90deg and 90deg. I think we can both agree that it would be way more useful to get values between 0deg and 360deg. Luckily, we can transform the asin() range by first multiplying its output by 2 so values are between -180deg and 180deg, then shifting it by 180deg so we get values from 0deg to 360deg:
.element {
rotate: calc(asin(var(--progress)) * 2 + 180deg);
}
The last thing we would like to do is avoid working with a --progress input from -1 to 1. Progress is often measure from 0 to 1 rather starting in negative territory like -1 and 1. To do so, we can map an input between -1 to 1 to an input from 0 to 1 like so:
.element {
rotate: calc((asin(var(--progress) * 2 - 1) * 2) + 180deg);
}
Which works similarly to how we mapped the angles: we multiply by 2 to get inputs between 0 and 2, then shift them by -1 so they are between -1 and 1.
Using asin() as a Bezier curve
You may be wondering why we did all those transformations to asin() ion the last section. Well, first, asin() is kinda useless as-is since we can only invert numbers from -1 to 1 and get angles between -90deg and 90deg. We can wiggle out of these limitations with the last transformation.
However, you may also ask in which situations we could use asin(), since converting a progress variable between 0 and 1 to an angle between 0deg and 360deg is usually as easy as:
.element {
rotate: calc(var(--progress) * 360deg);
}
Well, notice that the last snippet rotates the element at a linear rate. If we want to make it spin at a different rate, we could use a Bezier curve (which, admittedly, is simpler), or we could take the fun route and notice that our last asin() transformation is also a Bezier curve! To see that, let’s graph it again with the transformations:

Remember that a Bezier curve takes an input between 0 and 1 and returns and output between 0 and 1 too. And our asin() transformation also receives an input between 0 and 1 but it already outputs an angle between 0deg and 360deg!
So, for example, we could make spin animations with a little rush at the end using asin(). To do so, we’ll first set up a progress @property and animate it between 0 to 1:
@property --progress {
syntax: "<number>";
initial-value: 0;
inherits: false;
}
@keyframes progress {
from {
--progress: 0;
}
to {
--progress: 1;
}
}
We define --progress with @property so we can interpolate it with CSS animations.
Then we can spin elemenets following asin() curve:
ul {
rotate: calc((asin(var(--progress) * 2 - 1) * 2) + 180deg);
animation: progress 5s linear infinite;
}
Specification
The asin() function is defined in the CSS Values and Units Module Level 4 specification, which is currently in Editor’s Draft.
Browser support
More information
- Trigonometric functions in CSS (Bramus)
- 081: Trigonometric Functions (CSS Podcast)
Related tricks!
Creating a Clock with the New CSS sin() and cos() Trigonometry Functions
The “Most Hated” CSS Feature: cos() and sin()
The “Most Hated” CSS Feature: tan()
The “Most Hated” CSS Feature: asin(), acos(), atan() and atan2()
Related
acos()
.element { rotate: acos(0.5) }
atan()
.element { rotate: atan(1); }
atan2()
.element { rotate: atan2(200px, 200px); }
sin()
.element { transform: translateY(calc(sin(20deg * var(--i)) * 100px)); }
cos()
.element { transform: translateY(calc(cos(20deg * var(--i)) * 100px)); }
tan()
.element { transform: translateY(calc(tan(15deg * var(--i)) * 5dvh)); }