<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:media="http://search.yahoo.com/mrss/"
	
	>

<channel>
	<title>Nimā Bahre(h)mand</title>
	<link>https://nimabahrehmand.com</link>
	<description>Nimā Bahre(h)mand</description>
	<pubDate>Fri, 07 Nov 2025 00:15:25 +0000</pubDate>
	<generator>https://nimabahrehmand.com</generator>
	<language>en</language>
	
		
	<item>
		<title>ppl</title>
				
		<link>https://nimabahrehmand.com/ppl</link>

		<pubDate>Fri, 07 Nov 2025 00:15:25 +0000</pubDate>

		<dc:creator>Nimā Bahre(h)mand</dc:creator>

		<guid isPermaLink="true">https://nimabahrehmand.com/ppl</guid>

		<description>

Papers — Black Hole Circles

  :root{ --vh:1vh; }
  html,body{
    margin:0;padding:0;height:100%;
    background:#fff; overflow:hidden;
    overscroll-behavior:none;
    touch-action:none;
    -webkit-user-select:none; user-select:none;
    -webkit-tap-highlight-color:transparent;
  }
  #link-canvas{
    position:fixed; inset:0; z-index:1; pointer-events:none;
    display:block; width:100%; height:100%;
  }
  #sheet-layer{
    position:fixed; inset:0; z-index:2; overflow:hidden;
    contain: layout paint size;
  }
  .sheet{
    position:absolute; top:0; left:0;
    transform:translate(-10000px,-10000px) rotate(0rad) scale(1);
    transform-origin:50% 50%;
    will-change:transform,visibility;
    backface-visibility:hidden;
    visibility:hidden;
    user-select:none;
    pointer-events:auto;
  }
  .hole{
    border-radius:50%;
    background: radial-gradient(closest-side, #000 0%, #000 10%, rgba(0,0,0,0) 100%);
    box-shadow: 0 0 5px 5px rgba(0,0,0,0);
  }
  .hole.link-hole{
    background: radial-gradient(closest-side, #0000ff 0%, #0000ff 10%, rgba(0,0,255,0) 100%);
  }

  #paper-overlay{ position:fixed; inset:0; display:none; z-index:5; pointer-events:none; }
  #overlay-center{
    position:absolute; left:0; top:0; transform:translate(-50%, -50%);
    width:min(520px, 62vw);
    max-height:min(46vh, calc(var(--vh)*46));
    padding:16px; box-sizing:border-box;
    background:#fff; border:2px solid #000; overflow:auto; border-radius:0;
  }
  #overlay-text{
    font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;
    font-weight:700; color:#0000ff; text-align:center; letter-spacing:.005em;
    font-size:clamp(16px, 2.5vmin, 30px); line-height:1.3;
    white-space:normal; word-break:keep-all; overflow-wrap:break-word;
    -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
  }
  #overlay-text img{
    max-width:100%;
    height:auto;
    display:block;
    margin:0 auto;
  }






  



(()=&#62; {
  const BASE_W = 27;
  const DEPTH_MINMAX = [0.85, 1.10];
  const MAX_SPEED = 0.14;
  const ROT_SPEED = { min:-0.0015, max:0.0015 };
  const DRAG = 0.002;
  const SWAY = 0.012;
  const NEIGHBORS = 9;
  const DIST_FACTOR = 1.0;
  const LINE_ALPHA = 0.28;
  const LINE_WIDTH = 1.05;
  const WOBBLE = 1;

  const TEXTS = [
    "the desert does not sleep... it vibrates with the memory of extraction",
    "screens flicker, holding only fragments of a world already lost",
    "we are not surrounded by things... we are surrounded by what they dis/replaced",
    { text: "Visit my project →", url: "https://nimabahrehmand.com/bst" },
    "zophobas morio eats plastic, digesting our future with quiet indifference",
    "a projection becomes wallpaper, then dissolves into silence... art as an atmosphere, not an object",
    "machines dream in memories that never belonged to them",
    "every time the screen goes dark, a new world begins",
    "i have a snowflake in my left eye, melting every time i try to see",
    "oil becomes rumor... rumor becomes law",
    "every collapse has a rhythm... every rhythm asks to be seen",
    "i recorded a silence so loud it jammed the frame",
    "collective imagination as resistance",
    "the archive is restless... every image mutates, demanding to be seen differently... again and again",
    "the looping screensaver, mistaken for immortality",
    "myth... the first algorithm translating fear into pattern... into control",
    "ai of the multitude to unlearn obedience",
    "angels are falling in slow motion... their wings pixelated... suspended forever between descent and deletion",
    {
      type: "gif",
      src: "https://freight.cargo.site/t/original/i/ac0a4ddedbb3bf88b282d139c5bb45b186fa92f211d63f5423f1bacf6c4aeab5/EOOC_004.gif"
    }
  ];

  const layer = document.getElementById('sheet-layer');
  const linkCv = document.getElementById('link-canvas');
  const linkCtx = linkCv.getContext('2d', { alpha:true, desynchronized:true });
  const overlay = document.getElementById('paper-overlay');
  const overlayCenter = document.getElementById('overlay-center');
  const overlayText = document.getElementById('overlay-text');

  const clamp=(v,a,b)=&#62;Math.max(a,Math.min(b,v));
  const rand=(a,b)=&#62;a+Math.random()*(b-a);
  const VW=()=&#62; (window.visualViewport?.width  &#124;&#124; window.innerWidth);
  const VH=()=&#62; (window.visualViewport?.height &#124;&#124; window.innerHeight);

  function sizeLinkCanvas(){
    const dpr = Math.max(1, Math.min(2, window.devicePixelRatio &#124;&#124; 1));
    const w = VW(), h = VH();
    linkCv.width  = Math.round(w * dpr);
    linkCv.height = Math.round(h * dpr);
    linkCv.style.width  = w + 'px';
    linkCv.style.height = h + 'px';
    linkCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
  }
  sizeLinkCanvas();
  addEventListener('resize', sizeLinkCanvas);

  const pointer = { x:0, y:0 };
  let overlayVisible = false;
  let activePaper = null;

  addEventListener('pointermove', (e)=&#62;{
    pointer.x = e.clientX; pointer.y = e.clientY;
    if (overlayVisible){
      placeOverlayAt(pointer.x, pointer.y);
      if (activePaper &#38;&#38; activePaper.followPointer){
        activePaper.setCenter(pointer.x, pointer.y);
      }
    }
  }, {passive:true});

  function createHole(){
    const el = document.createElement('div');
    el.className = 'sheet hole';
    el.setAttribute('role','img');
    el.setAttribute('aria-label','black hole');
    layer.appendChild(el);
    return el;
  }

  function randomUnit(){ const a = rand(0, Math.PI*2); return { x: Math.cos(a), y: Math.sin(a) }; }

  class Paper {
    constructor(el, text){
      this.el = el;
      this.text = text &#124;&#124; "";
      this.spawn(true);
      el.addEventListener('pointerdown', (ev)=&#62;{
        ev.stopPropagation();
        this.onActivate();
      }, {passive:true});
    }

    updateSize(){
      this.h = this.w;
      this.el.style.width  = Math.round(this.w) + 'px';
      this.el.style.height = Math.round(this.h) + 'px';
    }

    setCenter(cx, cy){
      this.x = cx - this.w/2;
      this.y = cy - this.h/2;
    }

    spawn(initial=false){
      const W = VW(), H = VH();
      if (initial &#124;&#124; this.w == null){
        this.z = rand(DEPTH_MINMAX[0], DEPTH_MINMAX[1]);
        this.w = BASE_W * this.z * rand(0.9,1.1);
        this.updateSize();
      }
      const dir = randomUnit();
      const speed = rand(0.02, MAX_SPEED) * (this.z &#124;&#124; 1);
      this.x = rand(0, W - this.w);
      this.y = rand(0, H - this.h);
      this.vx = dir.x * speed;
      this.vy = dir.y * speed;
      if (initial){
        this.rot  = rand(-Math.PI, Math.PI);
        this.vrot = rand(ROT_SPEED.min, ROT_SPEED.max);
      }
      this.followPointer = false;
      this.el.style.visibility = 'visible';
    }

    get center(){ return { cx: this.x + this.w/2, cy: this.y + this.h/2 }; }

    onActivate(){
      if (this.text &#38;&#38; typeof this.text === "object" &#38;&#38; this.text.url) {
        window.open(this.text.url, "_blank");
        return;
      }

      if (overlayVisible &#38;&#38; activePaper === this){
        activePaper.followPointer = false;
        activePaper = null; hideText(); return;
      }
      activePaper = this;
      this.followPointer = true;
      showText(this.text);
      placeOverlayAt(pointer.x, pointer.y);
      this.setCenter(pointer.x, pointer.y);
    }

    update(dt){
      const W = VW(), H = VH();
      const tnow = performance.now()/1000;

      if (!this.followPointer){
        const jitterX = Math.sin(tnow*1.2) * SWAY * 0.5 * this.z;
        const jitterY = Math.cos(tnow*1.1) * SWAY * 0.5 * this.z;

        this.vx *= (1 - DRAG); this.vy *= (1 - DRAG);
        this.x += (this.vx + jitterX) * (dt*60);
        this.y += (this.vy + jitterY) * (dt*60);
        this.rot += this.vrot * (dt*60);

        if (this.x &#62; W) this.x = -this.w;
        if (this.x + this.w &#60; 0) this.x = W;
        if (this.y &#62; H) this.y = -this.h;
        if (this.y + this.h &#60; 0) this.y = H;
      } else {
        this.rot *= 0.98;
        this.vx = this.vy = 0;
      }
    }

    draw(){
      this.el.style.transform =
        `translate(${Math.round(this.x)}px, ${Math.round(this.y)}px) rotate(${this.rot}rad)`;
    }

    destroy(){ this.el.remove(); }
  }

  const sheets = [];

  function syncCirclesToTexts(){
    while (sheets.length &#60; TEXTS.length){
      const data = TEXTS[sheets.length];
      const el = createHole();

      if (typeof data === "object" &#38;&#38; data.url) {
        el.classList.add('link-hole');
      }

      const p = new Paper(el, data);
      sheets.push(p);
    }

    for (let i=0;i TEXTS.length){
      const p = sheets.pop();
      p.destroy();
    }
  }

  layer.innerHTML = '';
  syncCirclesToTexts();
  setInterval(syncCirclesToTexts, 1000);

  function showText(text){
    if (typeof text === "object") {
      if (text.type === "gif" &#38;&#38; text.src) {
        overlayText.innerHTML = `&#60;img src="${text.src}"&#62;`;
      } else if (text.text) {
        overlayText.textContent = String(text.text);
      } else {
        overlayText.textContent = "";
      }
    } else {
      overlayText.textContent = String(text &#124;&#124; "");
    }
    overlay.style.display = 'block';
    overlayVisible = true;
  }

  function hideText(){
    overlay.style.display='none';
    overlayVisible = false;
  }

  function placeOverlayAt(x, y){
    const W = VW(), H = VH();
    const w = overlayCenter.offsetWidth  &#124;&#124; 420;
    const h = overlayCenter.offsetHeight &#124;&#124; 220;
    overlayCenter.style.left = clamp(x, w*0.5 + 4, W - w*0.5 - 4) + 'px';
    overlayCenter.style.top  = clamp(y, h*0.5 + 4, H - h*0.5 - 4) + 'px';
  }

  addEventListener('pointerdown', (e)=&#62;{
    if (!overlayVisible) return;
    if (!(e.target &#38;&#38; e.target.classList &#38;&#38; e.target.classList.contains('hole'))){
      if (activePaper){ activePaper.followPointer = false; activePaper = null; }
      hideText();
    }
  });

  function nse(t, seed){
    return Math.sin(t*0.9 + seed*12.9898) * 0.5 + Math.cos(t*1.3 + seed*78.233) * 0.5;
  }

  function drawWobble(ac, bc, seed, t){
    const ax = ac.cx, ay = ac.cy;
    const bx = bc.cx, by = bc.cy;
    const dx = bx - ax, dy = by - ay;
    const len = Math.hypot(dx, dy) &#124;&#124; 1;
    const nx = -dy/len, ny = dx/len;
    const amp = (8 + Math.min(60, len*0.08)) * WOBBLE;
    const c1 = {
      x: ax + dx*0.33 + nx * amp * (nse(t*1.15, seed+0.2)),
      y: ay + dy*0.33 + ny * (amp * (nse(t*0.95, seed+0.7)))
    };
    const c2 = {
      x: ax + dx*0.66 + nx * amp * (nse(t*1.25, seed+1.2)),
      y: ay + dy*0.66 + ny * (amp * (nse(t*1.05, seed+1.7)))
    };
    linkCtx.beginPath();
    linkCtx.moveTo(ax, ay);
    linkCtx.bezierCurveTo(c1.x, c1.y, c2.x, c2.y, bx, by);
    linkCtx.stroke();
  }

  function drawConnections(){
    const n = sheets.length;
    linkCtx.clearRect(0,0,VW(),VH());
    if (n &#60; 2) return;
    const t = performance.now()/1000;
    const maxDist = Math.max(VW(), VH()) * DIST_FACTOR;
    linkCtx.lineWidth = LINE_WIDTH;
    linkCtx.strokeStyle = '#000';
    linkCtx.globalAlpha = LINE_ALPHA;
    const centers = sheets.map(s =&#62; s.center);
    const drawn = new Set();
    for (let i=0;i</description>
		
	</item>
		
	</channel>
</rss>