dupliter balloon accelerator ver 3

by dupliter

HTML

<div id="dupliter-container">
  <h1>Dupliter Temporal Balloon</h1>
  <p>Visualize past, present, and future states of an object with Dupliter. Adjust the sliders to explore temporal displacement and echo opacity.</p>
  
  <svg id="balloonCanvas" width="600" height="400"></svg>

  <div class="sliders">
    <label>Temporal Displacement:
      <input type="range" id="displacementSlider" min="0" max="150" value="0">
    </label>
    <label>Echo Opacity:
      <input type="range" id="opacitySlider" min="0.1" max="1" value="0.4" step="0.05">
    </label>
  </div>

  <h2>Dupliter Temporal Pulse Example</h2>
  <svg width="200" height="200" id="pulseCanvas"></svg>
</div>

CSS

body {
  font-family: "Segoe UI", sans-serif;
  text-align: center;
  background-color: #f9f9f9;
  padding: 20px;
}

#dupliter-container {
  display: inline-block;
}

#balloonCanvas, #pulseCanvas {
  border: 1px solid black;
  background: white;
  display: block;
  margin: 20px auto;
}

.sliders {
  display: flex;
  justify-content: center;
  gap: 20px;
  flex-wrap: wrap;
}

input[type=range] {
  width: 180px;
  margin: 10px;
}

JavaScript

// --- Dupliter Balloon ---

const svg = document.getElementById("balloonCanvas");
const displacementSlider = document.getElementById("displacementSlider");
const opacitySlider = document.getElementById("opacitySlider");

// SVG blur filter for echoes
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
const filter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
filter.setAttribute("id", "blur1");
const feGaussianBlur = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
feGaussianBlur.setAttribute("stdDeviation", "6");
filter.appendChild(feGaussianBlur);
defs.appendChild(filter);
svg.appendChild(defs);

function createCircle(cx, cy, r, fill, blur=0) {
  const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
  circle.setAttribute("cx", cx);
  circle.setAttribute("cy", cy);
  circle.setAttribute("r", r);
  circle.setAttribute("fill", fill);
  if (blur) circle.setAttribute("filter", `url(#blur1)`);
  return circle;
}

function createLabel(cx, cy, text) {
  const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
  label.setAttribute("x", cx);
  label.setAttribute("y", cy + 5); // center vertically
  label.setAttribute("text-anchor", "middle");
  label.setAttribute("fill", "white");
  label.setAttribute("font-size", "14");
  label.textContent = text;
  return label;
}

function drawBalloon(displacement, opacity) {
  svg.querySelectorAll("circle, text").forEach(el => el.remove());

  // Past echo
  svg.appendChild(createCircle(300 - displacement, 200, 60, `rgba(255,0,0,${opacity})`, 1));
  svg.appendChild(createLabel(300 - displacement, 200, "Past"));

  // Present
  svg.appendChild(createCircle(300, 200, 60, "red"));
  svg.appendChild(createLabel(300, 200, "Present"));

  // Future echo
  svg.appendChild(createCircle(300 + displacement, 200, 60, `rgba(255,0,0,${opacity})`, 1));
 ...