Interested in this project?

Lux Gatherings – Physics Light String

  • Skills

    Development, Branding, Design

  • Year

    2015

Challenge

A new startup that my agency had been working with since the beginning, looking to build an online portal for hiring servers and catering for luxury home parties was looking for a way to announce their new brand, and build hype. Their whole brand experience was based around; being an internet company (so we knew we had to have something somewhat advanced), and catering to a higher end client (thus the name “Lux Gatherings”).

Solution

So I took to building a beautifully minimalistic landing page to help communicate those two ideas. I worked with our designer to put together a simple layout, with a slight gradient, and this beautiful string light effect.

With the initial design in place I knew exactly what I wanted to do; animate the string lights. But not a static animation, I wanted them to be interactive.

So I settled on creating a physics based string lights effect. I found a great little 2D rigid-body physics engine in vanilla Javascript. This allowed me to create a rigid chain of small circular bodies, the small bodies would be set to not render. Meaning, it was a rigid line, connected to an invisible circle, then to another rigid line, and so on. Then at each invisible circle, I create a new, larger circle, connected (via a constraint, visualized as a thicker black line) to the invisible circle. Then I randomly positioned those larger, visible circles so that when the page was loaded, it would have a nice, falling effect.

  function initString() {
    var groupId = Body.nextGroupId();

    var stack = Composites.stack(0, -10, 10, 1, 0, 0, function(x, y, column, row) {
      var isStatic = false;

      x = ((column/9) * $string.width());
      y = (column/9) * 200;

      if (column == 0) {
        x = 50;
        y = -10;

        isStatic = true;
      }

      if (column == 9) {

        isStatic = true;
      }

      return Bodies.circle(x, y, 5, {
        groupId: groupId,
        isStatic: isStatic,
        frictionAir: 0.3,
        render: {
          visible: false,
        }
      });
    });

    string = Composites.chain(stack, 0, 0, 0, 0, {
      groupId: groupId,
      stiffness: 0.9,
      render: {
        strokeStyle: "#231f20",
        lineWidth: 3
      }
    });

    // add string
    World.add(engine.world, string);
  }

Now. I could have created more points along the chain, giving the rope a more fluid feel, but I hedged away from this, due to the fact that in my cross browser testing, I noticed that some browsers would slow down dramatically due to the added physics calculations.

Now I wanted to give the user a hint that they could interact with it. I came up with the idea of doing a wind effect. The problem was Matter did not have any sort of additional force effects. So I decided to shift gravity. At a random interval (between 0 and 3 seconds), and at a random duration (between 0 and 3 seconds), and at a random intensity (between 0.25 and 0.5), and at a random duration (between 10 and 20 frames), and in a random direction (half the length), I would ease the gravity from straight down, to the chosen direction, imitating a gust of wind.

  function initWind() {
    wind = window.setTimeout(function() { 
      gust();
    }, 2000);

    function gust() {
      var gravity = 0;
      var i = 0;

      var intensity = 0.25 - (Math.random() * 0.5);
      var length = Math.ceil(Math.random() * 20) + 10;
      var halflength = length/2;

      guster = window.setInterval(function() {
        if (i <= halflength) {
          gravity = ease(i, 0, intensity*halflength, halflength);
        } else {
          gravity = ease(i - halflength, intensity*halflength, 0 - intensity*halflength, halflength);
        }

        if (i == length) {
          window.clearInterval(guster);

          wind = window.setTimeout(function() {
            gust();
          }, Math.random() * 3000);
        }

        i++;

        engine.world.gravity.x = gravity;
      }, 100);
    }
  }

Then the last part, creating interaction. For desktop I created an invisible circle that would simply track with the mouse, and repel other circles.

function initMouse() {
  var mouser = Matter.MouseConstraint.create(engine, {
    constraint: {
      render: {
        visible: false
      }
    }
  });

  var groupId2 = Body.nextGroupId();
  var mover = Bodies.circle(0, 0, 10, {
    groupId: groupId2,
    isStatic: true,
    render: {
      visible: false,
    }
  });

  var mover2 = Bodies.circle(0, 0, 20, {
    groupId: groupId2,
    isStatic: false,
    render: {
      visible: false,
    }
  });

  var mouseConstraint = Constraint.create({
    bodyA: mover,
    bodyB: mover2,
    length: 0,
    render: {
      visible: false,
    }
  });

  Matter.Events.on(engine, 'mousemove', function(event) {
    mover.position = {x: event.mouse.position.x, y: event.mouse.position.y};
  });

  World.add(engine.world, [mouser, mover, mover2, mouseConstraint]);
}

And for Mobile I removed the wind, and instead opted to shift the gravity in the direction the user tilted their phone.

function initMobile() {
  $("#string").css("pointer-events", "none");

  window.addEventListener('deviceorientation', updateGravity, true);

  function updateGravity() {
    oldorient = orientation;
    orientation = window.orientation,
    gravity = engine.world.gravity;

    if (orientation != oldorient)
      resize();

    if (orientation === 0) {
      gravity.x = Common.clamp(event.gamma, -90, 90) / 90;
      gravity.y = Common.clamp(event.beta, -90, 90) / 90;
    } else if (orientation === 180) {
      gravity.x = Common.clamp(event.gamma, -90, 90) / 90;
      gravity.y = Common.clamp(-event.beta, -90, 90) / 90;
    } else if (orientation === 90) {
      gravity.x = Common.clamp(event.beta, -90, 90) / 90;
      gravity.y = Common.clamp(-event.gamma, -90, 90) / 90;
    } else if (orientation === -90) {
      gravity.x = Common.clamp(-event.beta, -90, 90) / 90;
      gravity.y = Common.clamp(event.gamma, -90, 90) / 90;
    }
  };
}

Creation

All In all, I created a very simplistic, beautiful way to grasp peoples attention, instigate interaction, and increase their likelihood of signing up to be a member of their mailing list. Thus increasing the companies likelihood of launch success.