# XY oscilloscope

> Lissajous figures glow in green phosphor over the graticule of an oscilloscope, and the pointer shifts their phase, on canvas.

- Category: [Backgrounds](https://library.aimran.es/components/patterns)
- Built with: HTML + CSS
- Live editor: https://library.aimran.es/blocks/pt-uvq16-oscilloscope
- Library: AIMRAN Library (https://library.aimran.es), free, no sign-up, by Aimran Oukham

## Where to use it

Two sine waves fed into the X and Y inputs of an old analog scope. Each frame draws the full curve three times with additive blending, a wide faint glow, a thinner beam and a bright core, while a translucent fill leaves a phosphor afterglow. The frequency ratio changes every few seconds, from 3 to 2 to 5 to 4 and more, with a soft fade.

The first color is the phosphor, the second the hot core of the beam and the third the graticule lines, with major divisions and fine ticks on the axes. Move the pointer sideways to turn the phase. Fits audio gear, synth makers, electronics and lab themed sites.

## Code

### HTML

```html
<div class="pt-uvq16-oscilloscope"><canvas></canvas><i class="gr"></i><i class="gl"></i></div>
```

### CSS

```css
.pt-uvq16-oscilloscope {
  --c1: #39ff88;
  --c2: #eaffd0;
  --c3: #7c8a84;
  --k: 1;
  /* velocidad */
  --d: 1;
  /* densidad / escala */
  --o: 1;
  /* opacidad */
  --bg: #05060a;
  --fg: #ffffff;
  --blend: screen;
  min-height: 100vh;
}

.pt-uvq16-oscilloscope {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: var(--bg);
  container-type: size;
  isolation: isolate;
}

.pt-uvq16-oscilloscope>* {
  opacity: var(--o);
}

.pt-uvq16-oscilloscope canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.pt-uvq16-oscilloscope {
  background: #040e09;
}

.pt-uvq16-oscilloscope .gr {
  position: absolute;
  inset: 6%;
  border: 1px solid color-mix(in srgb,var(--c3) 60%,transparent);
  border-radius: 6px;
  background-image: linear-gradient(color-mix(in srgb,var(--c3) 30%,transparent) 1px,transparent 1px),linear-gradient(90deg,color-mix(in srgb,var(--c3) 30%,transparent) 1px,transparent 1px);
  background-size: 10% 12.5%;
  background-position: -.5px -.5px;
  pointer-events: none;
}

.pt-uvq16-oscilloscope .gr::before,.pt-uvq16-oscilloscope .gr::after {
  content: "";
  position: absolute;
}

.pt-uvq16-oscilloscope .gr::before {
  left: 0;
  right: 0;
  top: 50%;
  height: 9px;
  translate: 0 -50%;
  background: repeating-linear-gradient(90deg,color-mix(in srgb,var(--c3) 70%,transparent) 0 1px,transparent 1px 2%);
}

.pt-uvq16-oscilloscope .gr::after {
  top: 0;
  bottom: 0;
  left: 50%;
  width: 9px;
  translate: -50% 0;
  background: repeating-linear-gradient(color-mix(in srgb,var(--c3) 70%,transparent) 0 1px,transparent 1px 2.5%);
}

.pt-uvq16-oscilloscope .gl {
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 95% at 50% 50%,transparent 58%,rgba(0,0,0,.6)),linear-gradient(155deg,rgba(255,255,255,.08),transparent 38%);
  pointer-events: none;
}
```

### JavaScript

```js
(function (el) {
const cv=el.querySelector("canvas"),x=cv.getContext("2d");let W=0,H=0,raf=0,LF=null,pend=null,vis=true,dead=false,RF=0;
const RM=matchMedia("(prefers-reduced-motion: reduce)").matches;
const fit=()=>{const r=Math.min(window.devicePixelRatio||1,2),w=el.clientWidth,h=el.clientHeight;if(w===W&&h===H&&cv.width===Math.round(w*r))return;W=w;H=h;cv.width=Math.round(w*r);cv.height=Math.round(h*r);x.setTransform(r,0,0,r,0,0);if(RM&&LF){RF=0;LF()}};
const ro=new ResizeObserver(fit);ro.observe(el);fit();
const io=new IntersectionObserver(e=>{vis=e[e.length-1].isIntersecting;if(vis&&pend&&!dead){const f=pend;pend=null;raf=window.requestAnimationFrame(f)}});io.observe(el);
const requestAnimationFrame=f=>{LF=f;if(dead)return 0;if(RM){if(++RF<90)f();return 0}if(!vis){pend=f;return 0}return window.requestAnimationFrame(f)};
const V=()=>{const s=getComputedStyle(el),g=n=>s.getPropertyValue(n).trim();return{c1:g("--c1"),c2:g("--c2"),c3:g("--c3"),bg:g("--bg")||"#05060a",k:+g("--k")||1,d:+g("--d")||1}};
const pc=document.createElement("canvas").getContext("2d"),CC={};
const rgb=c=>{if(CC[c])return CC[c];pc.fillStyle="#000";pc.fillStyle=c;const s=pc.fillStyle;return CC[c]=s[0]==="#"?[1,3,5].map(i=>parseInt(s.slice(i,i+2),16)):s.slice(s.indexOf("(")+1).split(",").slice(0,3).map(parseFloat)};
const mix=(a,b,p)=>[a[0]+(b[0]-a[0])*p,a[1]+(b[1]-a[1])*p,a[2]+(b[2]-a[2])*p];
const css=(a,o)=>o===undefined?"rgb("+(a[0]|0)+","+(a[1]|0)+","+(a[2]|0)+")":"rgba("+(a[0]|0)+","+(a[1]|0)+","+(a[2]|0)+","+o+")";
const lum=a=>(a[0]*.299+a[1]*.587+a[2]*.114)/255;const m={x:-1e4,y:-1e4,vx:0,vy:0,on:false};const mv=e=>{const r=el.getBoundingClientRect();if(!r.width)return;const nx=(e.clientX-r.left)*W/r.width,ny=(e.clientY-r.top)*H/r.height;if(m.on){m.vx=nx-m.x;m.vy=ny-m.y}m.x=nx;m.y=ny;m.on=true};const lv=()=>{m.on=false;m.x=m.y=-1e4;m.vx=m.vy=0};el.addEventListener("pointermove",mv);el.addEventListener("pointerdown",mv);el.addEventListener("pointerleave",lv);el.addEventListener("pointercancel",lv);
const RT=[[3,2],[5,4],[3,4],[1,2],[5,6],[2,3]];let t=0,ph=0,ri=0,rt=1,px=.5,first=true;
const loop=()=>{const v=V(),scr=[4,14,9];t+=.01*v.k;rt+=.01*v.k;if(rt>7){rt=0;ri=(ri+1)%RT.length}
const al=Math.max(0,Math.min(1,rt/.8,(7-rt)/.8));x.fillStyle=css(scr,first?1:.16);first=false;x.fillRect(0,0,W,H);
if(m.on)px+=(m.x/W-px)*.1;else px+=(.5-px)*.02;ph+=.012*v.k;
const [a,b]=RT[ri],cx=W/2,cy=H/2,Ax=Math.min(W*.4,H*.42),Ay=Math.min(H*.38,W*.42),dp=ph+(px-.5)*6;
x.beginPath();for(let q=0;q<=480;q++){const s=q/480*6.2832,X=cx+Ax*Math.sin(a*s+dp),Y=cy+Ay*Math.sin(b*s);q?x.lineTo(X,Y):x.moveTo(X,Y)}
const C1=rgb(v.c1),C2=rgb(v.c2);x.globalCompositeOperation="lighter";x.lineJoin="round";x.strokeStyle=css(C1,.09*al);x.lineWidth=7;x.stroke();x.strokeStyle=css(C1,.35*al);x.lineWidth=2.6;x.stroke();x.strokeStyle=css(C2,.8*al);x.lineWidth=1;x.stroke();
const s=(t*2.2)%6.2832,bx=cx+Ax*Math.sin(a*s+dp),by=cy+Ay*Math.sin(b*s),gd=x.createRadialGradient(bx,by,0,bx,by,10);gd.addColorStop(0,css(C2,.9*al));gd.addColorStop(1,css(C1,0));x.fillStyle=gd;x.fillRect(bx-10,by-10,20,20);x.globalCompositeOperation="source-over";
raf=requestAnimationFrame(loop)};loop();
return()=>{dead=true;cancelAnimationFrame(raf);ro.disconnect();io.disconnect();el.removeEventListener("pointermove",mv);el.removeEventListener("pointerdown",mv);el.removeEventListener("pointerleave",lv);el.removeEventListener("pointercancel",lv)};
})(document.querySelector(".pt-uvq16-oscilloscope"));
```

### React

```tsx
// @ts-nocheck
import { useEffect, useRef } from "react";

const css = `
.pt-uvq16-oscilloscope {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: var(--bg);
  container-type: size;
  isolation: isolate;
}

.pt-uvq16-oscilloscope>* {
  opacity: var(--o);
}

.pt-uvq16-oscilloscope canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.pt-uvq16-oscilloscope {
  background: #040e09;
}

.pt-uvq16-oscilloscope .gr {
  position: absolute;
  inset: 6%;
  border: 1px solid color-mix(in srgb,var(--c3) 60%,transparent);
  border-radius: 6px;
  background-image: linear-gradient(color-mix(in srgb,var(--c3) 30%,transparent) 1px,transparent 1px),linear-gradient(90deg,color-mix(in srgb,var(--c3) 30%,transparent) 1px,transparent 1px);
  background-size: 10% 12.5%;
  background-position: -.5px -.5px;
  pointer-events: none;
}

.pt-uvq16-oscilloscope .gr::before,.pt-uvq16-oscilloscope .gr::after {
  content: "";
  position: absolute;
}

.pt-uvq16-oscilloscope .gr::before {
  left: 0;
  right: 0;
  top: 50%;
  height: 9px;
  translate: 0 -50%;
  background: repeating-linear-gradient(90deg,color-mix(in srgb,var(--c3) 70%,transparent) 0 1px,transparent 1px 2%);
}

.pt-uvq16-oscilloscope .gr::after {
  top: 0;
  bottom: 0;
  left: 50%;
  width: 9px;
  translate: -50% 0;
  background: repeating-linear-gradient(color-mix(in srgb,var(--c3) 70%,transparent) 0 1px,transparent 1px 2.5%);
}

.pt-uvq16-oscilloscope .gl {
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 95% at 50% 50%,transparent 58%,rgba(0,0,0,.6)),linear-gradient(155deg,rgba(255,255,255,.08),transparent 38%);
  pointer-events: none;
}

`;

export default function PatternUvq16Oscilloscope() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
const cv=el.querySelector("canvas"),x=cv.getContext("2d");let W=0,H=0,raf=0,LF=null,pend=null,vis=true,dead=false,RF=0;
const RM=matchMedia("(prefers-reduced-motion: reduce)").matches;
const fit=()=>{const r=Math.min(window.devicePixelRatio||1,2),w=el.clientWidth,h=el.clientHeight;if(w===W&&h===H&&cv.width===Math.round(w*r))return;W=w;H=h;cv.width=Math.round(w*r);cv.height=Math.round(h*r);x.setTransform(r,0,0,r,0,0);if(RM&&LF){RF=0;LF()}};
const ro=new ResizeObserver(fit);ro.observe(el);fit();
const io=new IntersectionObserver(e=>{vis=e[e.length-1].isIntersecting;if(vis&&pend&&!dead){const f=pend;pend=null;raf=window.requestAnimationFrame(f)}});io.observe(el);
const requestAnimationFrame=f=>{LF=f;if(dead)return 0;if(RM){if(++RF<90)f();return 0}if(!vis){pend=f;return 0}return window.requestAnimationFrame(f)};
const V=()=>{const s=getComputedStyle(el),g=n=>s.getPropertyValue(n).trim();return{c1:g("--c1"),c2:g("--c2"),c3:g("--c3"),bg:g("--bg")||"#05060a",k:+g("--k")||1,d:+g("--d")||1}};
const pc=document.createElement("canvas").getContext("2d"),CC={};
const rgb=c=>{if(CC[c])return CC[c];pc.fillStyle="#000";pc.fillStyle=c;const s=pc.fillStyle;return CC[c]=s[0]==="#"?[1,3,5].map(i=>parseInt(s.slice(i,i+2),16)):s.slice(s.indexOf("(")+1).split(",").slice(0,3).map(parseFloat)};
const mix=(a,b,p)=>[a[0]+(b[0]-a[0])*p,a[1]+(b[1]-a[1])*p,a[2]+(b[2]-a[2])*p];
const css=(a,o)=>o===undefined?"rgb("+(a[0]|0)+","+(a[1]|0)+","+(a[2]|0)+")":"rgba("+(a[0]|0)+","+(a[1]|0)+","+(a[2]|0)+","+o+")";
const lum=a=>(a[0]*.299+a[1]*.587+a[2]*.114)/255;const m={x:-1e4,y:-1e4,vx:0,vy:0,on:false};const mv=e=>{const r=el.getBoundingClientRect();if(!r.width)return;const nx=(e.clientX-r.left)*W/r.width,ny=(e.clientY-r.top)*H/r.height;if(m.on){m.vx=nx-m.x;m.vy=ny-m.y}m.x=nx;m.y=ny;m.on=true};const lv=()=>{m.on=false;m.x=m.y=-1e4;m.vx=m.vy=0};el.addEventListener("pointermove",mv);el.addEventListener("pointerdown",mv);el.addEventListener("pointerleave",lv);el.addEventListener("pointercancel",lv);
const RT=[[3,2],[5,4],[3,4],[1,2],[5,6],[2,3]];let t=0,ph=0,ri=0,rt=1,px=.5,first=true;
const loop=()=>{const v=V(),scr=[4,14,9];t+=.01*v.k;rt+=.01*v.k;if(rt>7){rt=0;ri=(ri+1)%RT.length}
const al=Math.max(0,Math.min(1,rt/.8,(7-rt)/.8));x.fillStyle=css(scr,first?1:.16);first=false;x.fillRect(0,0,W,H);
if(m.on)px+=(m.x/W-px)*.1;else px+=(.5-px)*.02;ph+=.012*v.k;
const [a,b]=RT[ri],cx=W/2,cy=H/2,Ax=Math.min(W*.4,H*.42),Ay=Math.min(H*.38,W*.42),dp=ph+(px-.5)*6;
x.beginPath();for(let q=0;q<=480;q++){const s=q/480*6.2832,X=cx+Ax*Math.sin(a*s+dp),Y=cy+Ay*Math.sin(b*s);q?x.lineTo(X,Y):x.moveTo(X,Y)}
const C1=rgb(v.c1),C2=rgb(v.c2);x.globalCompositeOperation="lighter";x.lineJoin="round";x.strokeStyle=css(C1,.09*al);x.lineWidth=7;x.stroke();x.strokeStyle=css(C1,.35*al);x.lineWidth=2.6;x.stroke();x.strokeStyle=css(C2,.8*al);x.lineWidth=1;x.stroke();
const s=(t*2.2)%6.2832,bx=cx+Ax*Math.sin(a*s+dp),by=cy+Ay*Math.sin(b*s),gd=x.createRadialGradient(bx,by,0,bx,by,10);gd.addColorStop(0,css(C2,.9*al));gd.addColorStop(1,css(C1,0));x.fillStyle=gd;x.fillRect(bx-10,by-10,20,20);x.globalCompositeOperation="source-over";
raf=requestAnimationFrame(loop)};loop();
return()=>{dead=true;cancelAnimationFrame(raf);ro.disconnect();io.disconnect();el.removeEventListener("pointermove",mv);el.removeEventListener("pointerdown",mv);el.removeEventListener("pointerleave",lv);el.removeEventListener("pointercancel",lv)};
  }, []);
  return (
    <>
      <style>{css}</style>
      <div
        ref={ref}
        className="pt-uvq16-oscilloscope"
        style={{
          "--c1": "#39ff88",
          "--c2": "#eaffd0",
          "--c3": "#7c8a84",
          "--k": 1,
          "--d": 1,
          "--o": 1,
          "--bg": "#05060a",
          "--fg": "#ffffff",
          "--blend": "screen",
          minHeight: "100vh",
        }}
      >
        <canvas></canvas><i className="gr"></i><i className="gl"></i>
      </div>
    </>
  );
}
```

## Prompt for AI assistants

Paste this into ChatGPT, Claude, Cursor, v0 or Lovable to adapt the block to your stack (Tailwind, Vue, Svelte…):

````text
I want to add the “XY oscilloscope” block (Backgrounds) from AIMRAN Library to my project.

## What it is
Lissajous figures glow in green phosphor over the graticule of an oscilloscope, and the pointer shifts their phase, on canvas.

## Chosen settings
- Color 1: #39ff88
- Color 2: #eaffd0
- Color 3: #7c8a84
- Speed: 1x (from 0.2 to 3x)
- Density / scale: 1x (from 0.5 to 2x)
- Opacity: 1 (from 0.2 to 1)

## What I need
1. Add it to my project with the same look and animation as the reference code: same sizes, colours, radii, shadows, timings and easing.
2. It is built with plain HTML and CSS (the reference wraps it in a React component with its CSS). If my project uses something else (Vue, Svelte, Astro, plain HTML…), translate it without losing any behaviour. If I use Tailwind, turn the styles into classes; if not, keep the CSS in its own file.
3. Expose the settings above as props (or CSS variables) with those values as defaults.
4. Make it work well on phones: nothing that depends only on hover, touch targets of at least 44 px and no overflow on 320 px screens.
5. Make it look right in light and dark mode.
6. Keep it accessible: semantic HTML, roles and aria-* where needed, visible focus and keyboard support.
7. Respect prefers-reduced-motion: with reduced motion, no big animations but the same end result.

## Reference code
```tsx
// @ts-nocheck
import { useEffect, useRef } from "react";

const css = `
.pt-uvq16-oscilloscope {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: var(--bg);
  container-type: size;
  isolation: isolate;
}

.pt-uvq16-oscilloscope>* {
  opacity: var(--o);
}

.pt-uvq16-oscilloscope canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.pt-uvq16-oscilloscope {
  background: #040e09;
}

.pt-uvq16-oscilloscope .gr {
  position: absolute;
  inset: 6%;
  border: 1px solid color-mix(in srgb,var(--c3) 60%,transparent);
  border-radius: 6px;
  background-image: linear-gradient(color-mix(in srgb,var(--c3) 30%,transparent) 1px,transparent 1px),linear-gradient(90deg,color-mix(in srgb,var(--c3) 30%,transparent) 1px,transparent 1px);
  background-size: 10% 12.5%;
  background-position: -.5px -.5px;
  pointer-events: none;
}

.pt-uvq16-oscilloscope .gr::before,.pt-uvq16-oscilloscope .gr::after {
  content: "";
  position: absolute;
}

.pt-uvq16-oscilloscope .gr::before {
  left: 0;
  right: 0;
  top: 50%;
  height: 9px;
  translate: 0 -50%;
  background: repeating-linear-gradient(90deg,color-mix(in srgb,var(--c3) 70%,transparent) 0 1px,transparent 1px 2%);
}

.pt-uvq16-oscilloscope .gr::after {
  top: 0;
  bottom: 0;
  left: 50%;
  width: 9px;
  translate: -50% 0;
  background: repeating-linear-gradient(color-mix(in srgb,var(--c3) 70%,transparent) 0 1px,transparent 1px 2.5%);
}

.pt-uvq16-oscilloscope .gl {
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 95% at 50% 50%,transparent 58%,rgba(0,0,0,.6)),linear-gradient(155deg,rgba(255,255,255,.08),transparent 38%);
  pointer-events: none;
}

`;

export default function PatternUvq16Oscilloscope() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
const cv=el.querySelector("canvas"),x=cv.getContext("2d");let W=0,H=0,raf=0,LF=null,pend=null,vis=true,dead=false,RF=0;
const RM=matchMedia("(prefers-reduced-motion: reduce)").matches;
const fit=()=>{const r=Math.min(window.devicePixelRatio||1,2),w=el.clientWidth,h=el.clientHeight;if(w===W&&h===H&&cv.width===Math.round(w*r))return;W=w;H=h;cv.width=Math.round(w*r);cv.height=Math.round(h*r);x.setTransform(r,0,0,r,0,0);if(RM&&LF){RF=0;LF()}};
const ro=new ResizeObserver(fit);ro.observe(el);fit();
const io=new IntersectionObserver(e=>{vis=e[e.length-1].isIntersecting;if(vis&&pend&&!dead){const f=pend;pend=null;raf=window.requestAnimationFrame(f)}});io.observe(el);
const requestAnimationFrame=f=>{LF=f;if(dead)return 0;if(RM){if(++RF<90)f();return 0}if(!vis){pend=f;return 0}return window.requestAnimationFrame(f)};
const V=()=>{const s=getComputedStyle(el),g=n=>s.getPropertyValue(n).trim();return{c1:g("--c1"),c2:g("--c2"),c3:g("--c3"),bg:g("--bg")||"#05060a",k:+g("--k")||1,d:+g("--d")||1}};
const pc=document.createElement("canvas").getContext("2d"),CC={};
const rgb=c=>{if(CC[c])return CC[c];pc.fillStyle="#000";pc.fillStyle=c;const s=pc.fillStyle;return CC[c]=s[0]==="#"?[1,3,5].map(i=>parseInt(s.slice(i,i+2),16)):s.slice(s.indexOf("(")+1).split(",").slice(0,3).map(parseFloat)};
const mix=(a,b,p)=>[a[0]+(b[0]-a[0])*p,a[1]+(b[1]-a[1])*p,a[2]+(b[2]-a[2])*p];
const css=(a,o)=>o===undefined?"rgb("+(a[0]|0)+","+(a[1]|0)+","+(a[2]|0)+")":"rgba("+(a[0]|0)+","+(a[1]|0)+","+(a[2]|0)+","+o+")";
const lum=a=>(a[0]*.299+a[1]*.587+a[2]*.114)/255;const m={x:-1e4,y:-1e4,vx:0,vy:0,on:false};const mv=e=>{const r=el.getBoundingClientRect();if(!r.width)return;const nx=(e.clientX-r.left)*W/r.width,ny=(e.clientY-r.top)*H/r.height;if(m.on){m.vx=nx-m.x;m.vy=ny-m.y}m.x=nx;m.y=ny;m.on=true};const lv=()=>{m.on=false;m.x=m.y=-1e4;m.vx=m.vy=0};el.addEventListener("pointermove",mv);el.addEventListener("pointerdown",mv);el.addEventListener("pointerleave",lv);el.addEventListener("pointercancel",lv);
const RT=[[3,2],[5,4],[3,4],[1,2],[5,6],[2,3]];let t=0,ph=0,ri=0,rt=1,px=.5,first=true;
const loop=()=>{const v=V(),scr=[4,14,9];t+=.01*v.k;rt+=.01*v.k;if(rt>7){rt=0;ri=(ri+1)%RT.length}
const al=Math.max(0,Math.min(1,rt/.8,(7-rt)/.8));x.fillStyle=css(scr,first?1:.16);first=false;x.fillRect(0,0,W,H);
if(m.on)px+=(m.x/W-px)*.1;else px+=(.5-px)*.02;ph+=.012*v.k;
const [a,b]=RT[ri],cx=W/2,cy=H/2,Ax=Math.min(W*.4,H*.42),Ay=Math.min(H*.38,W*.42),dp=ph+(px-.5)*6;
x.beginPath();for(let q=0;q<=480;q++){const s=q/480*6.2832,X=cx+Ax*Math.sin(a*s+dp),Y=cy+Ay*Math.sin(b*s);q?x.lineTo(X,Y):x.moveTo(X,Y)}
const C1=rgb(v.c1),C2=rgb(v.c2);x.globalCompositeOperation="lighter";x.lineJoin="round";x.strokeStyle=css(C1,.09*al);x.lineWidth=7;x.stroke();x.strokeStyle=css(C1,.35*al);x.lineWidth=2.6;x.stroke();x.strokeStyle=css(C2,.8*al);x.lineWidth=1;x.stroke();
const s=(t*2.2)%6.2832,bx=cx+Ax*Math.sin(a*s+dp),by=cy+Ay*Math.sin(b*s),gd=x.createRadialGradient(bx,by,0,bx,by,10);gd.addColorStop(0,css(C2,.9*al));gd.addColorStop(1,css(C1,0));x.fillStyle=gd;x.fillRect(bx-10,by-10,20,20);x.globalCompositeOperation="source-over";
raf=requestAnimationFrame(loop)};loop();
return()=>{dead=true;cancelAnimationFrame(raf);ro.disconnect();io.disconnect();el.removeEventListener("pointermove",mv);el.removeEventListener("pointerdown",mv);el.removeEventListener("pointerleave",lv);el.removeEventListener("pointercancel",lv)};
  }, []);
  return (
    <>
      <style>{css}</style>
      <div
        ref={ref}
        className="pt-uvq16-oscilloscope"
        style={{
          "--c1": "#39ff88",
          "--c2": "#eaffd0",
          "--c3": "#7c8a84",
          "--k": 1,
          "--d": 1,
          "--o": 1,
          "--bg": "#05060a",
          "--fg": "#ffffff",
          "--blend": "screen",
          minHeight: "100vh",
        }}
      >
        <canvas></canvas><i className="gr"></i><i className="gl"></i>
      </div>
    </>
  );
}
```

Give me the complete final code, ready to paste, and tell me in one line where to put it in my project.
````
