About this snippet

Engage your visitors with an interactive elastic string. As you drag the line with your mouse, we run a spring physics simulation to snap it back gracefully, which looks incredibly premium.

Size
3.0 KB
Uses
HTML, CSS, JavaScript
Loads
Nothing external
Views
264
<div class="elastic-container">
  <div class="content-overlay">
    <h2>Pull The String</h2>
    <p>Grab the glowing line below and release it to see the physics engine in action.</p>
  </div>
  
  <div class="svg-wrapper" id="string-wrapper">
    <svg viewBox="0 0 1000 500" width="100%" height="100%" preserveAspectRatio="none">
      <path id="elastic-line" d="M 0 250 Q 500 250 1000 250" fill="none" stroke="#3b82f6" stroke-width="6" stroke-linecap="round"></path>
    </svg>
  </div>
</div>
*, *::before, *::after {
  box-sizing: border-box;
}

body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  margin: 0;
  background: #0f172a;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  overflow: hidden;
}

.elastic-container {
  position: relative;
  width: 100%;
  max-width: 1000px;
  height: 500px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.content-overlay {
  position: absolute;
  top: 60px;
  text-align: center;
  color: #ffffff;
  pointer-events: none;
  z-index: 10;
}

.content-overlay h2 {
  font-size: 2.5rem;
  margin: 0 0 10px 0;
  letter-spacing: -1px;
}

.content-overlay p {
  color: #94a3b8;
  font-size: 1.125rem;
  margin: 0;
}

.svg-wrapper {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  cursor: grab;
}

.svg-wrapper:active {
  cursor: grabbing;
}

#elastic-line {
  filter: drop-shadow(0 0 15px rgba(59, 130, 246, 0.8));
}
const wrapper = document.getElementById("string-wrapper");
const line = document.getElementById("elastic-line");

let isDragging = false;
let mouseX = 500;
let mouseY = 250;

let currentY = 250;
let targetY = 250;
let velocity = 0;

// Physics constants
const tension = 0.05;
const friction = 0.85;

wrapper.addEventListener("pointerdown", function(e) {
  isDragging = true;
  wrapper.setPointerCapture(e.pointerId);
  updateMousePos(e);
});

wrapper.addEventListener("pointermove", function(e) {
  if (isDragging) {
    updateMousePos(e);
  }
});

function endDrag() {
  isDragging = false;
  targetY = 250;
  mouseX = 500;
}

wrapper.addEventListener("pointerup", endDrag);
wrapper.addEventListener("pointercancel", endDrag);

function updateMousePos(e) {
  const rect = wrapper.getBoundingClientRect();
  // Map mouse position to the 1000x500 SVG viewBox
  mouseX = (e.clientX - rect.left) * (1000 / rect.width);
  let rawY = (e.clientY - rect.top) * (500 / rect.height);
  
  targetY = rawY;
}

function animate() {
  if (!isDragging) {
    // Spring physics calculation
    const force = (targetY - currentY) * tension;
    velocity += force;
    velocity *= friction;
    currentY += velocity;
  } else {
    // Follow mouse smoothly when dragging
    currentY += (targetY - currentY) * 0.5;
  }
  
  // Update the Quadratic Bezier Curve path
  line.setAttribute("d", "M 0 250 Q " + mouseX + " " + currentY + " 1000 250");
  
  window.requestAnimationFrame(animate);
}

animate();
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save