Trigonometry in CSS and JavaScript: Getting Creative with Trigonometric Functions

In the second part of this series on trigonometry, we’ll explore JavaScript trigonometric functions and learn how we can apply them to our CSS code.

In part 1 we got an overview of trigonometry and learnt how we can use trigonometric functions in Sass. But for dynamic variables, we would be wise to move our calculations into JavaScript. Let’s take a look at an example that’s slightly more complex than clipping a simple triangle.

This article is the 2nd part in a series on “Trigonometry in CSS and JavaScript”:

  1. Introduction to Trigonometry
  2. Getting Creative with Trigonometric Functions (this article)
  3. Beyond Triangles

In the following demo we have a square-based pyramid, built with CSS 3D transforms. Using the slider, we can change the length of the sides of the pyramid, which results in changes to the overall height, and the angle of the sloping sides.

See the Pen Pyramids by Michelle Barker (@michellebarker) on CodePen.

To recalculate the angle at which the sides are rotated every time the input value changes, we need trigonometry. In order to do that, we can take a cross-section of the pyramid from the side, and visualize it as a triangle.

We can see that, just like our equilateral triangle in the previous article, the cross-section of our pyramid can be broken up into two right-angled triangles. (This time the shape of the cross-section is an isosceles triangle — a triangle that has two sides of equal length.)

To create the shapes for the base and sides of the pyramid, we can set the width and initial height, and use clip-path to clip the triangular shape of the sides.

.shape__base {
	--w: 10rem;
	
	width: var(--w);
	height: var(--w);
}

.shape__side {
	width: var(--side);
	height: var(--h, 20rem);
	clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

I’m using custom properties here because they allow us to easily reuse identical values. I’m setting a default value for the --h custom property for the height value of the shape side, as we’ll change this value later on with JavaScript. (This is the value we’ll get from the slider.)

Going back to our cross-section diagram, we can see that our known values are the opposite side (which will be half of our --w variable) and the hypotenuse (the --h variable). What is unknown is the angle at which we need to rotate the sides so that they meet in the middle.

If we imagine the side of the pyramid originates from a starting position in the center, the angle we need to calculate is the one at the top of the triangle. We can think of it as being a bit like leaning a ladder against a wall. The angle between the ladder and the wall is the one we need to calculate.

Again, we can use custom properties in our CSS to set some transform values. Each side will have the same rotateX() value (the angle we’re going to calculate), but different rotateY() values, as they’ll be rotated around the pyramid (represented by the --ry custom property here):

.shape__side {
	transform-origin: top center;
	transform: 	
		rotateY(var(--ry, 0)) 
		rotateX(var(--angle, 15deg));	
}

.shape__side:nth-child(2) {
	--ry: 90deg;
}
	
.shape__side:nth-child(3) {
	--ry: -90deg;
}

.shape__side:nth-child(4) {
	--ry: 180deg;
}

Calculating angles

In the previous article we saw how we can calculate the length of any side of a right-angled triangle if we know the angle, but how about calculating the angle itself? For that, we need to rearrange our equations.

We know the opposite side and the hypotenuse, which indicates we need to use the Sine function. Dividing the opposite by the hypotenuse gives us sin(ϴ):

sin(angle) = o / h
Thee trigonometric equations, abbreviated to SOHCAHTOA

Therefore the angle is calculated by the inverse Sine (or Arcsine) of the opposite divided by the hypotenuse:

Math functions

We can use JavaScript math functions for this. Let’s create a function to call whenever the input changes, and update the --h (for the hypotenuse) and --angle custom properties. To get the Arcsine value we use Math.asin():

const shape = document.querySelector('.shape')
const input = document.querySelector('[data-slider]')

const setAngles = () => {
	const o = shape.clientWidth / 2
	const h = input.value
	const angle = Math.asin(o / h)
	
	shape.style.setProperty('--h', `${h}px`)
	shape.style.setProperty('--angle', `${angle}rad`)
}

input.addEventListener('input', setAngles)

Radians versus degrees

You might notice that we’re setting the --angle custom property value in radians, not degrees. Unless you’re a mathematician, there’s a good chance you usually think of angles in terms of degrees, rather than radians. A radian can be visualized as the length of the radius of a circle wrapped around the circumference. There are 2pi radians in a circle.

Diagram illustrating 2pi radians in a circle

The Math.asin() function gives us the angle in radians, and radians are perfectly legitimate units in CSS, so this will work just fine. But if you prefer to set the value in degrees, we can convert them with a simple function:

const radToDeg = (radians) => {
	return radians * (180 / Math.PI)
}

In the demo I’m also rounding the resulting value to two decimal places with toFixed():

const setAngles = () => {
	const o = shape.clientWidth / 2
	const h = input.value
	const radians = Math.asin(o / h)
	const angle = radToDeg(radians).toFixed(2)
	
	shape.style.setProperty('--h', `${h}px`)
	shape.style.setProperty('--angle', `${angle}deg`)
}

Now the angles of the sides of our pyramid will be recalculated every time we move the slider to change the length of the sides.

Get creative

Using the same method, we could even create a bunch of pyramids of random heights, by changing a single custom property:

See the Pen Pyramids by Michelle Barker (@michellebarker) on CodePen.

Here’s another creative example of trigonometry in action: A paper snowflake maker, where the user can drag the handles to clip out segments of a triangle to generate the snowflake pattern. The clip path coordinates were calculated using trigonometric functions.

See the Pen Snowflakes with clip-path trigonometry by Michelle Barker (@michellebarker) on CodePen.

In the next article we’ll see how trigonometry affords us even more creative possibilities when combined with JS, by enabling us to plot polygons and more complex shapes.

Michelle Barker

Michelle is a Senior Front End Developer at Ada Mode, where she builds web apps and data visualisations for the renewable energy industry. She is the author of front-end blog CSS { In Real Life }, and has written articles for CSS Tricks, Smashing Magazine, and Web Designer Magazine, to name a few. She enjoys experimenting with new CSS features and helping others learn about them.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!