Signal Lab

Steal these effects.

The interactions that run this site — hand-written, dependency-free, and honest about their trade-offs. Every specimen below is one <script> you can paste into any page and it works.

no React · no npm · no CDN · no build step — vanilla, reduced-motion-aware: no eval, no inline JS handlers, no third-party host. MIT.

Running a strict Content-Security-Policy? Under script-src 'self' the inline <script> is blocked — move each snippet's script body into its own .js file and load it with <script src>. The code itself uses no eval and no third-party host, so it stays clean once externalized. Each specimen here also styles itself inline (a <style> block, a style= attribute, and/or a scripted .style write); this site permits that with style-src 'self' 'unsafe-inline' — if your style-src is stricter, adapt those too.

DotGrid

~1.4 KB JS

move the cursor · click to ripple

  • Howcanvas · idle-sleeping rAF · click = spring shockwave
  • Reduced-motionpointer:fine only; static grid under reduced-motion
  • Depsnone · no eval, no inline handlers, no third-party
  • LicenseMIT (see below)
copy · paste · it works
<!-- DotGrid · interactive spring dot-grid · vanilla · no eval/no third-party · MIT -->
<div id="dotgrid" style="position:relative;width:100%;height:280px"></div>
<script>
(function(){
  var host=document.getElementById('dotgrid'),cv=document.createElement('canvas');
  host.appendChild(cv);var ctx=cv.getContext('2d');if(!ctx)return;
  var GAP=26,DOT=2.4,PROX=110,ACCENT=[124,140,255],BASE=[130,138,160],
      reduce=matchMedia('(prefers-reduced-motion:reduce)').matches,
      fine=matchMedia('(pointer:fine)').matches,
      dpr=Math.min(devicePixelRatio||1,2),W,H,cols,rows,sx,sy,dots=[],px=-9e9,py=-9e9,raf,run=false,last=0;
  function build(){var r=host.getBoundingClientRect();W=r.width;H=r.height;
    cv.width=W*dpr|0;cv.height=H*dpr|0;cv.style.width=W+'px';cv.style.height=H+'px';ctx.setTransform(dpr,0,0,dpr,0,0);
    cols=Math.floor(W/GAP);rows=Math.floor(H/GAP);sx=(W-(cols-1)*GAP)/2;sy=(H-(rows-1)*GAP)/2;dots=[];
    for(var y=0;y<rows;y++)for(var x=0;x<cols;x++)dots.push({x:sx+x*GAP,y:sy+y*GAP,ox:0,oy:0,vx:0,vy:0});}
  function draw(){ctx.clearRect(0,0,W,H);for(var i=0;i<dots.length;i++){var p=dots[i],dx=p.x-px,dy=p.y-py,
    dd=dx*dx+dy*dy,t=dd<PROX*PROX?1-Math.sqrt(dd)/PROX:0;
    ctx.fillStyle=t?'rgba('+((BASE[0]+(ACCENT[0]-BASE[0])*t)|0)+','+((BASE[1]+(ACCENT[1]-BASE[1])*t)|0)+','+((BASE[2]+(ACCENT[2]-BASE[2])*t)|0)+','+(0.18+0.72*t)+')':'rgba(130,138,160,.18)';
    ctx.beginPath();ctx.arc(p.x+p.ox,p.y+p.oy,DOT,0,6.283);ctx.fill();}}
  function step(){var m=false;for(var i=0;i<dots.length;i++){var p=dots[i];if(p.ox||p.oy||p.vx||p.vy){
    p.vx+=(-170*p.ox-11*p.vx)/60;p.vy+=(-170*p.oy-11*p.vy)/60;p.ox+=p.vx/60;p.oy+=p.vy/60;
    if(Math.abs(p.ox)<.05&&Math.abs(p.oy)<.05&&Math.abs(p.vx)<.6){p.ox=p.oy=p.vx=p.vy=0;}else m=true;}}
    draw();if(m||performance.now()-last<120)raf=requestAnimationFrame(step);else run=false;}
  function kick(){if(!run){run=true;raf=requestAnimationFrame(step);}}
  build();draw();addEventListener('resize',function(){build();draw();},{passive:true});
  if(!reduce&&fine){
    host.addEventListener('pointermove',function(e){var r=cv.getBoundingClientRect();px=e.clientX-r.left;py=e.clientY-r.top;last=performance.now();kick();});
    host.addEventListener('pointerdown',function(e){var r=cv.getBoundingClientRect(),cx=e.clientX-r.left,cy=e.clientY-r.top;
      for(var i=0;i<dots.length;i++){var p=dots[i],dx=p.x-cx,dy=p.y-cy,dist=Math.hypot(dx,dy);
        if(dist<160){var f=1-dist/160,n=dist||1;p.vx+=(dx/n)*420*f*f;p.vy+=(dy/n)*420*f*f;}}last=performance.now();kick();});}
})();
</script>

Scramble text

~0.5 KB JS
Something worth reading.
  • Howfires once on scroll-in via IntersectionObserver
  • Reduced-motioninstant swap under reduced-motion; aria-hidden node + sr-only twin
  • Depsnone · no eval, no inline handlers, no third-party
  • LicenseMIT (see below)
copy · paste · it works
<!-- Scramble text · terminal-glyph settle on scroll-in · vanilla · MIT -->
<!-- a11y: the animated node is aria-hidden; a visually-hidden twin carries the stable text. -->
<span style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)">Something worth reading.</span>
<h2 id="scramble" aria-hidden="true">Something worth reading.</h2>
<script>
(function(){
  var el=document.getElementById('scramble'),final=el.textContent,
      reduce=matchMedia('(prefers-reduced-motion:reduce)').matches;
  function run(){
    if(reduce){el.textContent=final;return;}
    var g='01<>#%/\\+-=*',len=final.length,t0=0;
    (function tick(nw){if(!t0)t0=nw;var p=Math.min(1,(nw-t0)/700),fix=Math.round(p*len),o=final.slice(0,fix);
      for(var i=fix;i<len;i++){var c=final.charAt(i);o+=c===' '?' ':g.charAt(Math.random()*g.length|0);}
      el.textContent=o;if(p<1)requestAnimationFrame(tick);else el.textContent=final;})(performance.now());}
  if('IntersectionObserver'in window){var io=new IntersectionObserver(function(es){
    es.forEach(function(x){if(x.isIntersecting){io.unobserve(x.target);run();}});},{threshold:.6});io.observe(el);}
  else run();
})();
</script>

SpotlightCard

~0.3 KB JS + CSS
Hover me — the glow follows your cursor.
  • HowCSS radial-gradient fed --mx/--my
  • Reduced-motionoff under hover:none + reduced-motion
  • Depsnone · no eval, no inline handlers, no third-party
  • LicenseMIT (see below)
copy · paste · it works
<!-- SpotlightCard · cursor-following radial glow · CSS + 3 lines JS · MIT -->
<div class="spot">Hover me — the glow follows your cursor.</div>
<style>
  .spot{position:relative;overflow:hidden;padding:2.5rem;border-radius:14px;
    border:1px solid rgba(255,255,255,.12);background:#12141b;color:#e9ecf2}
  .spot::after{content:"";position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity .25s;
    background:radial-gradient(230px circle at var(--mx,50%) var(--my,50%),rgba(124,140,255,.18),transparent 66%)}
  .spot:hover::after{opacity:1}
  @media(hover:none),(prefers-reduced-motion:reduce){.spot::after{display:none}}
</style>
<script>
document.querySelectorAll('.spot').forEach(function(c){
  c.addEventListener('pointermove',function(e){var r=c.getBoundingClientRect();
    c.style.setProperty('--mx',(e.clientX-r.left)+'px');c.style.setProperty('--my',(e.clientY-r.top)+'px');},{passive:true});
});
</script>

Gauge ring

~0.4 KB JS
  • HowSVG stroke-dashoffset, CSS transition
  • Reduced-motiontransition respects reduced-motion (media override in the snippet)
  • Depsnone · no eval, no inline handlers, no third-party
  • LicenseMIT (see below)
copy · paste · it works
<!-- Gauge ring · animated SVG arc · CSS + tiny JS · a11y range · MIT -->
<svg width="130" height="130" viewBox="0 0 120 120" aria-hidden="true">
  <circle cx="60" cy="60" r="52" fill="none" stroke="rgba(255,255,255,.1)" stroke-width="9"/>
  <circle id="arc" cx="60" cy="60" r="52" fill="none" stroke="#7c8cff" stroke-width="9"
    stroke-linecap="round" transform="rotate(-90 60 60)"/>
  <text id="num" x="60" y="66" text-anchor="middle" fill="#e9ecf2"
    font-family="ui-monospace,monospace" font-size="20" font-weight="700">64%</text>
</svg>
<input id="range" type="range" min="0" max="100" value="64" aria-label="Gauge value">
<style>
  #arc{transition:stroke-dashoffset .5s}
  @media(prefers-reduced-motion:reduce){#arc{transition:none}}
</style>
<script>
(function(){
  var arc=document.getElementById('arc'),num=document.getElementById('num'),sl=document.getElementById('range'),
      C=2*Math.PI*52; arc.setAttribute('stroke-dasharray',C.toFixed(1));
  function set(v){arc.setAttribute('stroke-dashoffset',(C*(1-v/100)).toFixed(1));num.textContent=v+'%';
    sl.setAttribute('aria-valuetext',v+' percent');}
  sl.addEventListener('input',function(){set(+sl.value);});set(+sl.value);
})();
</script>

License

Every specimen is MIT — use them anywhere, commercial or not; keep the notice below. Colors are neutral placeholders (e.g. #7c8cff): swap the accent for your own. Built by the CoreGuard developer, the same way the rest of the site is: /humans.txt · view-source the homepage.

MIT License · Copyright (c) 2026 CoreGuard

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.