Warp any DOM element with this bookmarklet

Nov 19th, 2025

I've been working on a design and found a cool effect that would make a great bookmarklet. A bookmarklet is a JS snippet that you can drag to your bookmarks bar and run on any page.

Press ESC to cancel`;const closeBtn=document.createElement('button');closeBtn.textContent='✕ Close';closeBtn.style.cssText=`padding: 6px 12px;background: #ef4444;color: white;border: none;border-radius: 4px;cursor: pointer;font-weight: 500;font-size: 12px;margin-left: auto;`;closeBtn.onclick=cleanup;instructions.appendChild(instructionText);instructions.appendChild(closeBtn);document.body.appendChild(instructions);document.addEventListener('mousemove',onSelectorMove);document.addEventListener('click',onSelectorClick);document.addEventListener('keydown',(e)=>{if(e.key==='Escape')cleanup();});window.__bezierCleanup=()=>{instructions.remove();cleanup();};}function onSelectorMove(e){if(!selectorActive)return;const target=document.elementFromPoint(e.clientX,e.clientY);if(!target||target===highlightBox)return;const rect=target.getBoundingClientRect();highlightBox.style.display='block';highlightBox.style.left=rect.left+'px';highlightBox.style.top=rect.top+'px';highlightBox.style.width=rect.width+'px';highlightBox.style.height=rect.height+'px';}function onSelectorClick(e){if(!selectorActive)return;e.preventDefault();e.stopPropagation();const target=document.elementFromPoint(e.clientX,e.clientY);if(!target)return;selectorActive=false;highlightBox.style.display='none';document.removeEventListener('mousemove',onSelectorMove);document.removeEventListener('click',onSelectorClick);selectElement(target);}function selectElement(element){selectedElement=element;const rect=element.getBoundingClientRect();const scrollX=window.pageXOffset||document.documentElement.scrollLeft;const scrollY=window.pageYOffset||document.documentElement.scrollTop;transformedContainer=document.createElement('div');transformedContainer.style.cssText=`position: absolute;left: ${rect.left+scrollX}px;top: ${rect.top+scrollY}px;width: ${rect.width}px;height: ${rect.height}px;z-index: 10000;pointer-events: none;`;for(let i=0;i{const dot=createControlPoint(point,'left',index);overlay.appendChild(dot);});rightCurvePoints.forEach((point,index)=>{const dot=createControlPoint(point,'right',index);overlay.appendChild(dot);});updateControlPointPositions();drawCurves();document.body.appendChild(overlay);document.addEventListener('mousemove',onDrag);document.addEventListener('mouseup',stopDrag);}function createControlPoint(point,side,index){const dot=document.createElement('div');dot.style.cssText=`position: absolute;width: 14px;height: 14px;background: ${side==='left'?'#ef4444':'#3b82f6'};border: 2px solid white;border-radius: 50%;cursor: move;pointer-events: all;transform: translate(-50%, -50%);`;dot.dataset.side=side;dot.dataset.index=index;dot.addEventListener('mousedown',startDrag);point.element=dot;return dot;}function updateControlPointPositions(){leftCurvePoints.forEach(point=>{point.element.style.left=point.x+'px';point.element.style.top=point.y+'px';});rightCurvePoints.forEach(point=>{point.element.style.left=point.x+'px';point.element.style.top=point.y+'px';});}function drawCurves(){const svg=document.getElementById('bezier-svg');if(!svg)return;const existing=svg.querySelectorAll('path, line');existing.forEach(el=>el.remove());drawControlHandles(svg,leftCurvePoints,'#ef4444');drawControlHandles(svg,rightCurvePoints,'#3b82f6');const leftPath=createBezierPath(leftCurvePoints,'#ef4444');svg.appendChild(leftPath);const rightPath=createBezierPath(rightCurvePoints,'#3b82f6');svg.appendChild(rightPath);}function drawControlHandles(svg,points,color){const line1=document.createElementNS('http://www.w3.org/2000/svg','line');line1.setAttribute('x1',points[0].x);line1.setAttribute('y1',points[0].y);line1.setAttribute('x2',points[1].x);line1.setAttribute('y2',points[1].y);line1.setAttribute('stroke',color);line1.setAttribute('stroke-width','1');line1.setAttribute('stroke-dasharray','4,4');line1.setAttribute('opacity','0.4');svg.appendChild(line1);const line2=document.createElementNS('http://www.w3.org/2000/svg','line');line2.setAttribute('x1',points[2].x);line2.setAttribute('y1',points[2].y);line2.setAttribute('x2',points[3].x);line2.setAttribute('y2',points[3].y);line2.setAttribute('stroke',color);line2.setAttribute('stroke-width','1');line2.setAttribute('stroke-dasharray','4,4');line2.setAttribute('opacity','0.4');svg.appendChild(line2);}function createBezierPath(points,color){const path=document.createElementNS('http://www.w3.org/2000/svg','path');const d=`M ${points[0].x},${points[0].y} C ${points[1].x},${points[1].y} ${points[2].x},${points[2].y} ${points[3].x},${points[3].y}`;path.setAttribute('d',d);path.setAttribute('fill','none');path.setAttribute('stroke',color);path.setAttribute('stroke-width','2');path.setAttribute('opacity','0.6');return path;}function calculateSlicePoints(){slicePoints=[];for(let i=0;i{updateControlPointPositions();calculateSlicePoints();drawCurves();const rect=transformedContainer.getBoundingClientRect();updateAllTransforms(rect.width,rect.height);updateScheduled=false;});}}function stopDrag(){dragging=null;}function updateAllTransforms(width,height){for(let i=0;i[...row,b[i]]);for(let i=0;iMath.abs(aug[maxRow][i])){maxRow=k;}}[aug[i],aug[maxRow]]=[aug[maxRow],aug[i]];for(let k=i+1;k=0;i--){x[i]=aug[i][n];for(let j=i+1;j Install My Bookmarklet