<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">


  <meta charset="utf-8">
  <meta name='viewport' content='width=device-width, initial-scale=1'>

  <title>The Last Experience - Web Background</title>

  <link rel="stylesheet" media="screen" href="https://static.codepen.io/assets/fullpage/fullpage-4de243a40619a967c0bf13b95e1ac6f8de89d943b7fc8710de33f681fe287604.css" />
  <link href="https://fonts.googleapis.com/css?family=Lato:300,400,400italic,700,700italic,900,900italic" rel="stylesheet" />

  


  <title>CodePen - The Last Experience</title>
  <script>
  if (document.location.search.match(/type=embed/gi)) {
    window.parent.postMessage("resize", "*");
  }
</script>


  <style>
    html { font-size: 15px; }
    html, body { margin: 0; padding: 0; min-height: 100%; }
    body { height:100%; display: flex; flex-direction: column; }
    .referer-warning {
      background: black;
      box-shadow: 0 2px 5px rgba(0,0,0, 0.5);
      padding: 0.75em;
      color: white;
      text-align: center;
      font-family: 'Lato', 'Lucida Grande', 'Lucida Sans Unicode', Tahoma, Sans-Serif;
      line-height: 1.2;
      font-size: 1rem;
      position: relative;
      z-index: 2;
    }
    .referer-warning h1 { font-size: 1.2rem; margin: 0; }
    .referer-warning a { color: #56bcf9; } /* $linkColorOnBlack */
  </style>
</head>

<body class="">
  

  <div id="result-iframe-wrap" role="main">

    <iframe
      id="result"
      srcdoc="
<!DOCTYPE html>
<html lang=&quot;en&quot; >

<head>

  <meta charset=&quot;UTF-8&quot;>
  
<link rel=&quot;apple-touch-icon&quot; type=&quot;image/png&quot; href=&quot;https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png&quot; />
<meta name=&quot;apple-mobile-web-app-title&quot; content=&quot;CodePen&quot;>

<link rel=&quot;shortcut icon&quot; type=&quot;image/x-icon&quot; href=&quot;https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico&quot; />

<link rel=&quot;mask-icon&quot; type=&quot;&quot; href=&quot;https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg&quot; color=&quot;#111&quot; />


  <title>CodePen - The Last Experience</title>
  
  
  
  
<style>
body, html {
	position: absolute;
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100%;
	overflow: hidden;
}

canvas {
	position: absolute;
	width: 100%;
	height: 100%;
	background:#000;
	cursor: pointer;
}
</style>

  <script>
  window.console = window.console || function(t) {};
</script>

  
  
  <script>
  if (document.location.search.match(/type=embed/gi)) {
    window.parent.postMessage(&quot;resize&quot;, &quot;*&quot;);
  }
</script>


</head>

<body translate=&quot;no&quot; >
  <canvas></canvas><!-- 
o
 \_/\o
( Oo)                    \|/
(_=-)  .===O-  ~~Z~A~P~~ -O-
/   \_/U'                /|\
||  |_/
\\  |
{K ||
 | PP
 | ||
 (__\\


-->
    <script src=&quot;https://static.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js&quot;></script>

  
  
      <script id=&quot;rendered-js&quot; >
&quot;use strict&quot;;
///////////////// worker thread code ///////////////////
const theLastExperience = noWorkers => {
  &quot;use strict&quot;;
  // ---- robot structure ----
  const struct = {
    points: [
    {
      x: 0,
      y: -4,
      f(s, d) {
        this.y -= 0.01 * s * ts;
      } },

    {
      x: 0,
      y: -16,
      f(s, d) {
        this.y -= 0.02 * s * d * ts;
      } },

    {
      x: 0,
      y: 12,
      f(s, d) {
        this.y += 0.02 * s * d * ts;
      } },

    { x: -12, y: 0 },
    { x: 12, y: 0 },
    {
      x: -3,
      y: 34,
      f(s, d) {
        if (d > 0) {
          this.x += 0.01 * s * ts;
          this.y -= 0.015 * s * ts;
        } else {
          this.y += 0.02 * s * ts;
        }
      } },

    {
      x: 3,
      y: 34,
      f(s, d) {
        if (d > 0) {
          this.y += 0.02 * s * ts;
        } else {
          this.x -= 0.01 * s * ts;
          this.y -= 0.015 * s * ts;
        }
      } },

    {
      x: -28,
      y: 0,
      f(s, d) {
        this.x += this.vx * 0.025 * ts;
        this.y -= 0.001 * s * ts;
      } },

    {
      x: 28,
      y: 0,
      f(s, d) {
        this.x += this.vx * 0.025 * ts;
        this.y -= 0.001 * s * ts;
      } },

    {
      x: -3,
      y: 64,
      f(s, d) {
        this.y += 0.015 * s * ts;
        if (d > 0) {
          this.y -= 0.01 * s * ts;
        } else {
          this.y += 0.05 * s * ts;
        }
      } },

    {
      x: 3,
      y: 64,
      f(s, d) {
        this.y += 0.015 * s * ts;
        if (d > 0) {
          this.y += 0.05 * s * ts;
        } else {
          this.y -= 0.01 * s * ts;
        }
      } }],


    links: [
    { p0: 3, p1: 7, size: 12, lum: 0.5 },
    { p0: 1, p1: 3, size: 24, lum: 0.5 },
    { p0: 1, p1: 0, size: 60, lum: 0.5, disk: 1 },
    { p0: 5, p1: 9, size: 16, lum: 0.5 },
    { p0: 2, p1: 5, size: 32, lum: 0.5 },
    { p0: 1, p1: 2, size: 50, lum: 1 },
    { p0: 6, p1: 10, size: 16, lum: 1.5 },
    { p0: 2, p1: 6, size: 32, lum: 1.5 },
    { p0: 4, p1: 8, size: 12, lum: 1.5 },
    { p0: 1, p1: 4, size: 24, lum: 1.5 }] };


  class Robot {
    constructor(color, light, size, x, y, struct) {
      this.x = x;
      this.points = [];
      this.links = [];
      this.frame = 0;
      this.dir = 1;
      this.size = size;
      this.color = Math.round(color);
      this.light = light;
      // ---- create points ----
      for (const p of struct.points) {
        this.points.push(new Robot.Point(size * p.x + x, size * p.y + y, p.f));
      }
      // ---- create links ----
      for (const link of struct.links) {
        const p0 = this.points[link.p0];
        const p1 = this.points[link.p1];
        const dx = p0.x - p1.x;
        const dy = p0.y - p1.y;
        this.links.push(
        new Robot.Link(
        this,
        p0,
        p1,
        Math.sqrt(dx * dx + dy * dy),
        link.size * size / 3,
        link.lum,
        link.force,
        link.disk));


      }
    }
    update() {
      if (++this.frame % Math.round(20 / ts) === 0) this.dir = -this.dir;
      if (this === pointer.dancerDrag &amp;&amp; this.size < 16 &amp;&amp; this.frame > 600) {
        pointer.dancerDrag = null;
        dancers.push(
        new Robot(
        this.color + 90,
        this.light * 1.25,
        this.size * 2,
        pointer.x,
        pointer.y - 100 * this.size * 2,
        struct));


        dancers.sort(function (d0, d1) {
          return d0.size - d1.size;
        });
      }
      // ---- update links ----
      for (const link of this.links) link.update();
      // ---- update points ----
      for (const point of this.points) point.update(this);
      // ---- ground ----
      for (const link of this.links) {
        const p1 = link.p1;
        if (p1.y > canvas.height * ground - link.size * 0.5) {
          p1.y = canvas.height * ground - link.size * 0.5;
          p1.x -= p1.vx;
          p1.vx = 0;
          p1.vy = 0;
        }
      }
      // ---- center position ----
      this.points[3].x += (this.x - this.points[3].x) * 0.001;
    }
    draw() {
      for (const link of this.links) {
        if (link.size) {
          const dx = link.p1.x - link.p0.x;
          const dy = link.p1.y - link.p0.y;
          const a = Math.atan2(dy, dx);
          // ---- shadow ----
          ctx.save();
          ctx.translate(link.p0.x + link.size * 0.25, link.p0.y + link.size * 0.25);
          ctx.rotate(a);
          ctx.drawImage(
          link.shadow,
          -link.size * 0.5,
          -link.size * 0.5);

          ctx.restore();
          // ---- stroke ----
          ctx.save();
          ctx.translate(link.p0.x, link.p0.y);
          ctx.rotate(a);
          ctx.drawImage(
          link.image,
          -link.size * 0.5,
          -link.size * 0.5);

          ctx.restore();
        }
      }
    }}

  Robot.Link = class Link {
    constructor(parent, p0, p1, dist, size, light, force, disk) {
      this.p0 = p0;
      this.p1 = p1;
      this.distance = dist;
      this.size = size;
      this.light = light || 1.0;
      this.force = force || 0.5;
      this.image = this.stroke(
      &quot;hsl(&quot; + parent.color + &quot; ,30%, &quot; + parent.light * this.light + &quot;%)&quot;,
      true, disk, dist, size);

      this.shadow = this.stroke(&quot;rgba(0,0,0,0.5)&quot;, false, disk, dist, size);
    }
    update() {
      const p0 = this.p0;
      const p1 = this.p1;
      const dx = p1.x - p0.x;
      const dy = p1.y - p0.y;
      const dist = Math.sqrt(dx * dx + dy * dy);
      if (dist > 0.0) {
        const tw = p0.w + p1.w;
        const r1 = p1.w / tw;
        const r0 = p0.w / tw;
        const dz = (this.distance - dist) * this.force;
        const sx = dx / dist * dz;
        const sy = dy / dist * dz;
        p1.x += sx * r0;
        p1.y += sy * r0;
        p0.x -= sx * r1;
        p0.y -= sy * r1;
      }
    }
    stroke(color, axis, disk, dist, size) {
      let image;
      if (noWorkers) {
        image = document.createElement(&quot;canvas&quot;);
        image.width = dist + size;
        image.height = size;
      } else {
        image = new OffscreenCanvas(dist + size, size);
      }
      const ict = image.getContext(&quot;2d&quot;);
      ict.beginPath();
      ict.lineCap = &quot;round&quot;;
      ict.lineWidth = size;
      ict.strokeStyle = color;
      if (disk) {
        ict.arc(size * 0.5 + dist, size * 0.5, size * 0.5, 0, 2 * Math.PI);
        ict.fillStyle = color;
        ict.fill();
      } else {
        ict.moveTo(size * 0.5, size * 0.5);
        ict.lineTo(size * 0.5 + dist, size * 0.5);
        ict.stroke();
      }
      if (axis) {
        const s = size / 10;
        ict.fillStyle = &quot;#000&quot;;
        ict.fillRect(size * 0.5 - s, size * 0.5 - s, s * 2, s * 2);
        ict.fillRect(size * 0.5 - s + dist, size * 0.5 - s, s * 2, s * 2);
      }
      return image;
    }};

  Robot.Point = class Point {
    constructor(x, y, fn, w) {
      this.x = x;
      this.y = y;
      this.w = w || 0.5;
      this.fn = fn || null;
      this.px = x;
      this.py = y;
      this.vx = 0.0;
      this.vy = 0.0;
    }
    update(robot) {
      // ---- dragging ----
      if (robot === pointer.dancerDrag &amp;&amp; this === pointer.pointDrag) {
        this.x += (pointer.x - this.x) * 0.1;
        this.y += (pointer.y - this.y) * 0.1;
      }
      // ---- dance ----
      if (robot !== pointer.dancerDrag) {
        this.fn &amp;&amp; this.fn(16 * Math.sqrt(robot.size), robot.dir);
      }
      // ---- verlet integration ----
      this.vx = this.x - this.px;
      this.vy = this.y - this.py;
      this.px = this.x;
      this.py = this.y;
      this.vx *= 0.995;
      this.vy *= 0.995;
      this.x += this.vx;
      this.y += this.vy + 0.01 * ts;
    }};

  // ---- init ----
  const dancers = [];
  let ground = 1.0;
  let canvas = { width: 0, height: 0, resize: true };
  let ctx = null;
  let pointer = { x: 0, y: 0, dancerDrag: null, pointDrag: null };
  let ts = 1;
  let lastTime = 0;
  // ---- messages from the main thread ----
  const message = e => {
    switch (e.data.msg) {
      case &quot;start&quot;:
        canvas.elem = e.data.elem;
        canvas.width = canvas.elem.width;
        canvas.height = canvas.elem.height;
        ctx = canvas.elem.getContext(&quot;2d&quot;);
        initRobots();
        requestAnimationFrame(run);
        break;
      case &quot;resize&quot;:
        canvas.width = e.data.width;
        canvas.height = e.data.height;
        canvas.resize = true;
        break;
      case &quot;pointerMove&quot;:
        pointer.x = e.data.x;
        pointer.y = e.data.y;
        break;
      case &quot;pointerDown&quot;:
        pointer.x = e.data.x;
        pointer.y = e.data.y;
        for (const dancer of dancers) {
          for (const point of dancer.points) {
            const dx = pointer.x - point.x;
            const dy = pointer.y - point.y;
            const d = Math.sqrt(dx * dx + dy * dy);
            if (d < 60) {
              pointer.dancerDrag = dancer;
              pointer.pointDrag = point;
              dancer.frame = 0;
            }
          }
        }
        break;
      case &quot;pointerUp&quot;:
        pointer.dancerDrag = null;
        break;}

  };
  // ---- resize screen ----
  const resize = () => {
    canvas.elem.width = canvas.width;
    canvas.elem.height = canvas.height;
    canvas.resize = false;
    ground = canvas.height > 500 ? 0.85 : 1.0;
    for (let i = 0; i < dancers.length; i++) {
      dancers[i].x = (i + 2) * canvas.width / 9;
    }
  };
  // ---- main loop ----
  const run = time => {
    requestAnimationFrame(run);
    if (canvas.resize === true) resize();
    // ---- adjust speed to screen freq ----
    if (lastTime !== 0) {
      const t = (time - lastTime) / 16;
      ts += (t - ts) * 0.1;
      if (ts > 1) ts = 1;
    }
    lastTime = time;
    // ---- clear screen ----
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = &quot;#222&quot;;
    ctx.fillRect(0, 0, canvas.width, canvas.height * 0.15);
    ctx.fillRect(0, canvas.height * 0.85, canvas.width, canvas.height * 0.15);
    // ---- animate robots ----
    for (const dancer of dancers) {
      dancer.update();
      dancer.draw();
    }
  };
  const initRobots = () => {
    // ---- instanciate robots ----
    ground = canvas.height > 500 ? 0.85 : 1.0;
    for (let i = 0; i < 6; i++) {
      dancers.push(
      new Robot(
      i * 360 / 7,
      80,
      Math.sqrt(Math.min(canvas.width, canvas.height)) / 6,
      (i + 2) * canvas.width / 9,
      canvas.height * 0.5 - 100,
      struct));


    }
  };
  // ---- main thread vs. worker
  if (noWorkers) {
    // ---- emulate postMessage interface ----
    return {
      postMessage(data) {
        message({ data: data });
      } };

  } else {
    // ---- worker messaging ----
    onmessage = message;
  }
};
///////////////// main thread code ///////////////////
let worker = null;
const createWorker = fn => {
  const URL = window.URL || window.webkitURL;
  return new Worker(URL.createObjectURL(new Blob([&quot;(&quot; + fn + &quot;)()&quot;])));
};
// ---- init canvas ----
const canvas = document.querySelector(&quot;canvas&quot;);
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
// ---- instanciate worker ----
if (window.Worker &amp;&amp; window.OffscreenCanvas) {
  // instanciating background worker from a function
  worker = createWorker(theLastExperience);
  // cloning OffscreenCanvas
  const offscreen = canvas.transferControlToOffscreen();
  // sending data to worker
  worker.postMessage({ msg: &quot;start&quot;, elem: offscreen }, [offscreen]);
} else {
  // falling back execution to the main thread
  worker = theLastExperience(true);
  worker.postMessage({ msg: &quot;start&quot;, elem: canvas });
}
// ---- resize event ----
window.addEventListener(
&quot;resize&quot;,
() => {
  worker.postMessage({
    msg: &quot;resize&quot;,
    width: canvas.offsetWidth,
    height: canvas.offsetHeight });

},
false);

// ---- pointer events ----
const pointer = {
  x: 0,
  y: 0,
  down(e) {
    this.move(e);
    worker.postMessage({
      msg: &quot;pointerDown&quot;,
      x: this.x,
      y: this.y });

  },
  up(e) {
    worker.postMessage({
      msg: &quot;pointerUp&quot; });

  },
  move(e) {
    if (e.targetTouches) {
      e.preventDefault();
      this.x = e.targetTouches[0].clientX;
      this.y = e.targetTouches[0].clientY;
    } else {
      this.x = e.clientX;
      this.y = e.clientY;
    }
    worker.postMessage({
      msg: &quot;pointerMove&quot;,
      x: this.x,
      y: this.y });

  } };

window.addEventListener(&quot;mousemove&quot;, e => pointer.move(e), false);
canvas.addEventListener(&quot;touchmove&quot;, e => pointer.move(e), false);
window.addEventListener(&quot;mousedown&quot;, e => pointer.down(e), false);
window.addEventListener(&quot;touchstart&quot;, e => pointer.down(e), false);
window.addEventListener(&quot;mouseup&quot;, e => pointer.up(e), false);
window.addEventListener(&quot;touchend&quot;, e => pointer.up(e), false);
//# sourceURL=pen.js
    </script>

  

</body>

</html>
 
"
      sandbox="allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups allow-presentation  allow-scripts allow-top-navigation-by-user-activation" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; vr" allowTransparency="true"
      allowpaymentrequest="true" allowfullscreen="true" class="result-iframe">
      </iframe>

  </div>
</body>
</html>