<!-- Wrap the element in a container -->
<div class='resizer'>
  <div class='container'>
    <h1 data-splitting>Can’t stop looping</h1>
  </div>  
</div>

@import url('https://fonts.googleapis.com/css?family=Nova+Mono&display=swap');

* {
  box-sizing: border-box;
}

body {
  min-height: 100vh;
  background-color: #18181c;
  display: flex;
  align-items: center;
  justify-content: center;
}

h1 {
  font-family: 'Nova Mono', monospace;
  font-size: 2.5rem;
  text-transform: uppercase;
  width: 1109px;
  height: 365px;
  color: turquoise;
}

.char {
  offset-path: path(var(--path));
  offset-distance: calc(var(--char-index) * 1.5rem);
  animation: loop 1500ms cubic-bezier(.62,.01,.42,1.01) infinite alternate calc(var(--char-index) * 10ms);
}

@keyframes loop {
  50% {
    offset-distance: calc((var(--char-index) * 2.5rem) + 700px);
    color: hotpink;
  }
  100% {
    offset-distance: calc((var(--char-index) * 1.5rem) + 1690px);
  }
}

/* Bounds are 1109 x 365 */
/* Lets say a width of 75vmin */
.container {
  /* Ensure correct aspect ratio with this little trick */
  position: relative;
  height: 100%;
  width: 100%;
}

.resizer {
  overflow: hidden;
  padding: 2rem;
  resize: both;
  background: hsla(0, 0%, 100%, 0.15);
  height: calc((365 / 1109) * 75vmin);
  position: relative;
  width: 75vmin;
}
/* Use absolute positioning on the moving pieces and position top, left */
.char {
  top: 0;
  left: 0;
  position: absolute;
}
Splitting({
  whitespace: true
});

// Create a variable for the path
const path = 'M.2 219.6c247-107 233.3 91.4 406.4 138.4C659.2 426.6 750.2 6.6 552.2.6 337.7-5.9 426.9 415 696.1 359.4c256.1-52.9 182.1-217.9 413.1-163.9'
// Grab the container element
const container = document.querySelector(".container");

const responsivePath = new Meanderer({
  path: path,
  //  grabbed these by inspecing the h1 size but this would be the bounds
  width: 1109,
  height: 365,
})

// Update the path on the element via an inline CSS variable
// Do this whenever the container resizes using a ResizeObserver
const update = () => {
  const newPath = responsivePath.generatePath(
    container.offsetWidth,
    container.offsetHeight
  );
  container.style.setProperty('--path', `"${newPath}"`)
};

const SizeObserver = new ResizeObserver(update);
SizeObserver.observe(container);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/splitting@1.0.6/dist/splitting.min.js
  2. https://unpkg.com/meanderer@0.0.1/dist/meanderer.min.js