Pushing SCSS to its limits with math

I’ve always loved CSS and how well it performs with animations. I’ve had this one idea growing on me about creating visuals or landscapes created mathematically and be animated to some extent. Here is my first test on how well this works.

The Goal

There are several fancy JS libraries for creating stunning visuals, but I’ve never really been that hyped to do things via JS if you can do it with CSS. Thanks to having “long math” in high school, I remembered that you could create mathematical expressions and use that to generate visuals. For a first demo, I felt like a moving plane could be a good demo, so here are the specifications for the project:

  • Moving plane (matrix of HTML items)
  • Use mathematical expressions for some values
  • Interactivity

The inspiration

There is a visual work of art in a shopping mall close to me where lightbulbs move up and down. A single lightbulb isn’t anything magnificent, but having them all work together to create objects is really awesome. This is what led me to think about how this could be done in code, and from there, how this could be done in CSS.

How we are going to achieve it

CSS is quite limited and since we need to do mathematical calculations, it would be better to have the calculations be done when the client is loading the page. This can be achieved if we do the logic in SCSS and generate the proper values. By creating a matrix of items, targetting them with CSS nth-selector and doing the expressional calculation depending on matrix dot index, we can achieve mathematically calculated visuals.

The code

Here is the part of the code that does the magic:

// Expression: x^2 + 4
@for $r from 0 through 9 {
	$rowStart: ($r * 10 + 1);
	@for $c from 0 through 9 {
		  $columnStart: $rowStart + $c;
		  
		  // Work with row values
		  $valueX: pow($rowStart, 2) + 4;

		  @if $row == 0 {
			$valueX: 0;
		  }

		  // Work with column values
		  $valueY: pow($columnStart, 2) + 4;

		  $valueY: 0;

		  @if $column == 0 {
			$valueY: 0;
		  }

		  // Working on translate Z
		  div.dot:nth-child(#{$columnStart}) {
				.dot__inner {
					$hslColor: (($c + $r) * 15deg) % 360;

					animation-delay: $c * 0.1s + $r * 0.2s;
					animation-name: gradientWobble_ + $r + $c;

					// Hue
					background-color: hsl($hslColor, 50%, 50%);

					@keyframes #{gradientWobble_ + $r + $c} {
						0%   {transform: translateZ(0px);}
						50%  {transform: translateZ(($valueX + $valueY) * 0.025px);}
						100% {transform: translateZ(0px);}
					}
				}
		  }
	}
}

We are using the simple mathematical expression (x^2 + 4) where x is the index of the elements. As we traverse the indexes, the result from the expression changes and therefore generates a different translateZ value.

The colors are not made in any fancy but they are also based on the index. We calculate the HUE with (index-X + index-Y) times a modifier. With this, we get a rising HUE-value that we can set to a value between 0 and 360 with the help of modulo (HSL-Value % 360).

These 40 lines of code generate approx 2000 lines of CSS with this 10x10 grid. As we made the grid bigger, CSS-size increases quickly.

Demo

You might notice that I’ve used some JS but that is only for generating necessary amount of HTML-elements as I’m too lazy to do copy-paste 100 times.

See the Pen

Future thoughts

One thing lacking from this demo is interactivity. I’ve been thinking about using SCSS maps for easier handling of variables and move from CSS Animations to CSS Transitions. With CSS Transitions you could have certain previous elements affect what values the matrix-items should have with eg. “:focus” state, this way we can stay away from JS 😃