Creative Visual Effects

Deep-sea cursor trail effect

Create a pointer trail effect in a deep-sea scene: includes real content, actionable controls, clear state feedback, and layouts across sizes.

Creative Visual EffectsPointer Trail EffectsHTMLCanvasJavaScript
With JavaScript enabled, you can edit, run, and save this example. The fixed source is as follows:
<!doctype html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Deep-sea cursor trail effect</title>
  <style>
    :root { --ink:#18332f; --paper:#f4efe4; --accent:#d45f3e; --secondary:#8fc4ad; --line:color-mix(in srgb, var(--ink) 18%, transparent); --shadow:0 24px 60px color-mix(in srgb, var(--ink) 16%, transparent); color-scheme:light; }
    * { box-sizing:border-box; }
    html { min-width:320px; background:var(--paper); }
    body { min-height:100vh; margin:0; overflow-x:hidden; color:var(--ink); font-family:"IBM Plex Sans","PingFang SC",sans-serif; background:linear-gradient(color-mix(in srgb,var(--ink) 5%,transparent) 1px,transparent 1px),var(--paper);background-size:100% 36px; }
    button,input,select,textarea { color:inherit; font:inherit; }
    button,[role="button"],input,select,textarea { accent-color:var(--accent); }
    button { cursor:pointer; }
    button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible { outline:3px solid var(--accent); outline-offset:3px; }
    [hidden] { display:none !important; }
    @media (max-width:700px) { .mast h1 { line-height:1.08 !important; letter-spacing:-.035em !important; overflow-wrap:anywhere; } }
    @media (prefers-reduced-motion:reduce) { *,*::before,*::after { scroll-behavior:auto !important; animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; } }
    .page{width:min(1280px,calc(100% - 24px));margin:auto;padding:28px 0 64px}.mast{display:flex;justify-content:space-between;padding:18px;border:1px solid var(--line);background:color-mix(in srgb,#fff 60%,transparent);gap:18px;margin-bottom:14px}.mast h1{font-family:"IBM Plex Sans","PingFang SC",sans-serif;font-weight:800;font-size:clamp(34px,6vw,66px);letter-spacing:-.045em;margin:0;line-height:1}.eyebrow{display:block;margin-bottom:6px;color:var(--accent);font:800 10px/1 ui-monospace,monospace;letter-spacing:.16em}.intro{max-width:720px;margin:10px 0 0;font-size:14px;line-height:1.6}.scenario{padding:7px 10px;border:1px solid var(--line);border-radius:8px;background:var(--paper);font-size:12px}.demo{border-radius:14px;box-shadow:0 14px 40px color-mix(in srgb,var(--ink) 11%,transparent);position:relative;min-height:390px;overflow:hidden;padding:24px;background:color-mix(in srgb,var(--paper) 90%,#fff)}@media(max-width:700px){.page{width:calc(100% - 12px);padding-top:12px}.mast{display:grid}.demo{padding:12px}}
    .panel{padding:18px;border:3px solid var(--ink);background:var(--paper);box-shadow:8px 8px 0 var(--ink)}.stack{display:grid;gap:12px}.row{display:flex;flex-wrap:wrap;gap:8px}.action{min-height:42px;padding:8px 13px;border:3px solid var(--ink);background:var(--paper);font-weight:900}.action.primary,.action.is-active{color:var(--paper);background:var(--ink)}.status{min-height:24px;padding:8px;color:var(--paper);background:var(--ink);font-weight:900}.metric{font-size:clamp(34px,8vw,76px);font-weight:900;letter-spacing:-.08em}.muted{font-weight:600}.chip{padding:5px 8px;border:2px solid var(--ink);font-weight:900}.meter{height:14px;border:3px solid var(--ink)}.meter>i{display:block;height:100%;background:var(--ink)}
    .composition-index{position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:18px}.index-mark{display:grid;align-content:space-between;width:76px;padding:16px 10px;border:1px solid var(--line);color:var(--accent);background:color-mix(in srgb,var(--paper) 82%,transparent);text-align:center}.index-mark strong{font:900 34px/1 ui-monospace,monospace}.index-mark span{writing-mode:vertical-rl;font:800 10px/1.2 ui-monospace,monospace;letter-spacing:.18em}.composition-index .demo{grid-column:1;grid-row:1}.composition-index::before{content:"";position:absolute;left:-12px;top:38px;width:5px;height:45%;background:linear-gradient(var(--accent),var(--secondary))}@media(max-width:700px){.composition-index{grid-template-columns:1fr}.index-mark{grid-row:1;width:auto;grid-template-columns:auto 1fr}.index-mark span{writing-mode:horizontal-tb}.composition-index .demo{grid-row:2}}
    .cursor-trail-lab{display:grid;grid-template-columns:minmax(0,1.42fr) minmax(220px,.58fr);gap:16px}.trail-stage{position:relative;min-height:450px;overflow:hidden;border:1px solid var(--line);border-radius:24px;background:#070b18;color:#fff}.trail-stage canvas{position:absolute;inset:0;z-index:2;width:100%;height:100%;touch-action:none}.trail-stage>div{position:relative;z-index:1;max-width:590px;padding:clamp(30px,7vw,78px)}.trail-stage h2{font-size:clamp(36px,7vw,78px);line-height:.94}.trail-controls{display:grid;align-content:center;gap:17px}.trail-controls label{display:grid;grid-template-columns:1fr auto;gap:8px;font-weight:800}.trail-controls input{grid-column:1/-1}.cursor-trail-lab[data-trail-layout="garden-orbit"]{grid-template-columns:1fr}.cursor-trail-lab[data-trail-layout="garden-orbit"] .trail-stage{min-height:520px;border-radius:50% 50% 28px 28px;background:radial-gradient(circle at 50% 58%,#31572c,#101d16 48%,#050907 72%)}.cursor-trail-lab[data-trail-layout="garden-orbit"] .trail-stage>div{margin-left:auto;text-align:right}.cursor-trail-lab[data-trail-layout="garden-orbit"] .trail-controls{grid-template-columns:auto minmax(160px,1fr) minmax(160px,1fr) auto;align-items:end;border-radius:999px}.cursor-trail-lab[data-trail-layout="garden-orbit"] .trail-controls h2{grid-column:1/-1}.cursor-trail-lab[data-trail-layout="aurora-band"]{grid-template-columns:minmax(220px,.55fr) minmax(0,1.45fr)}.cursor-trail-lab[data-trail-layout="aurora-band"] .trail-stage{order:2;min-height:360px;border-radius:2px;background:linear-gradient(160deg,#071a2b,#123c49 55%,#39165c)}.cursor-trail-lab[data-trail-layout="aurora-band"] .trail-stage>div{max-width:470px}.cursor-trail-lab[data-trail-layout="aurora-band"] .trail-controls{border-left:8px solid var(--accent)}@media(max-width:740px){.cursor-trail-lab,.cursor-trail-lab[data-trail-layout="garden-orbit"],.cursor-trail-lab[data-trail-layout="aurora-band"]{grid-template-columns:1fr}.trail-stage{min-height:350px}.cursor-trail-lab[data-trail-layout="garden-orbit"] .trail-stage{min-height:410px;border-radius:50%}.cursor-trail-lab[data-trail-layout="garden-orbit"] .trail-controls{order:-1;grid-template-columns:1fr;border-radius:28px}.cursor-trail-lab[data-trail-layout="aurora-band"] .trail-stage{order:0;min-height:320px}}.workflow-dispatch{display:grid;grid-template-columns:minmax(210px,.48fr) minmax(0,1.52fr);gap:18px}.dispatch-console{display:grid;align-content:start;gap:16px;padding:22px;border:1px solid var(--line);border-radius:18px;background:color-mix(in srgb,var(--secondary) 12%,var(--paper))}.dispatch-console h2{margin:.2em 0}.dispatch-console form,.dispatch-console label{display:grid;gap:7px}.dispatch-console input,.dispatch-console select{min-width:0;width:100%;padding:10px;border:1px solid var(--line);border-radius:8px;background:var(--paper)}.dispatch-console fieldset{display:flex;gap:12px;border:1px solid var(--line);border-radius:10px}.dispatch-console fieldset label{display:flex;align-items:center}.dispatch-workbench{--dispatch-load:.22;min-width:0;padding:14px;border-left:8px solid color-mix(in srgb,var(--accent) calc(var(--dispatch-load)*100%),var(--line));background:linear-gradient(90deg,color-mix(in srgb,var(--accent) 8%,transparent),transparent 28%)}.dispatch-workbench[data-dispatch-priority="urgent"]{box-shadow:inset 0 0 0 2px var(--accent)}@media(max-width:900px){.workflow-dispatch{grid-template-columns:1fr}.dispatch-console{order:2}}
  </style>
</head>
<body data-aesthetic="dashboard" data-composition="field-index">
  <main class="page">
    <header class="mast"><div><span class="eyebrow">dashboard / Pointer Trail Effects / INTERMEDIATE</span><h1>Deep-sea cursor trail effect</h1><p class="intro">Create a pointer trail effect in a deep-sea scene: includes real content, actionable controls, clear state feedback, and layouts across sizes.</p></div><span class="scenario">Deep Sea</span></header>
    <div class="composition-index"><section class="demo" aria-label="Deep-sea pointer trail effect interaction area"><div class="workflow-dispatch"><aside class="dispatch-console"><span class="eyebrow">Deep Sea DISPATCH</span><h2>Submarine mission control</h2><form id="workflowDispatchForm"><label>Task Type<select id="workflowDispatchKind"><option>Sonar Scan</option><option>Hot Spring Sampling</option><option>Return Beacon</option></select></label><label>Subsea Depth<input id="workflowDispatchAmount" type="number" min="1" max="9999" value="45"><span>m</span></label><fieldset><legend>Processing priority</legend><label><input type="radio" name="workflowPriority" value="normal" checked>Regular</label><label><input type="radio" name="workflowPriority" value="urgent">Urgent</label></fieldset><button class="action primary" type="submit">Dive in</button></form><label>Current task<select id="workflowDispatchActive" aria-label="Switch current task"><option value="1">Sonar Scan</option><option value="2">Hot Spring Sampling</option></select></label><output id="workflowDispatchSummary">Waiting for Dispatch</output></aside><section id="workflowCore" class="workflow-core dispatch-workbench" data-dispatch-priority="normal"><!-- 11DZ_CORE_START:creative-effects-cursor-trail --><div class="cursor-trail-lab" data-trail-layout="deep-current"><section class="trail-stage"><canvas id="trailCanvas" width="960" height="460" tabindex="0" aria-label="Deep Sea Pointer Trail Canvas; move the pointer or use the arrow keys to draw trails" aria-describedby="trailHelp">Your browser does not support Canvas.</canvas><div><span>Deep Sea</span><h2>The current undertow carries every trace of movement into the depths</h2><p id="trailHelp">Move the mouse, touch the canvas, or focus it and use the arrow keys to draw.</p></div></section><aside class="trail-controls panel"><span class="eyebrow">CURSOR TRAIL</span><h2>Deep Sea Current Trail</h2><label>Light trail length <input id="trailLength" type="range" min="12" max="90" value="54" data-core-bridge="creative-effects-cursor-trail"><output id="trailLengthValue">54</output></label><label>Light Dot Size <input id="trailSize" type="range" min="3" max="18" value="9"><output id="trailSizeValue">9px</output></label><button id="trailClear" class="action" type="button">Clear light trail</button></aside></div><!-- 11DZ_CORE_END --></section></div><p id="status" class="status" role="status" aria-live="polite"></p></section><aside class="index-mark" aria-hidden="true"><strong>05</strong><span>Deep Sea FIELD INDEX</span></aside></div>
  </main>
  <script>
    'use strict';
    const statusOutput = document.querySelector('#status');
    const announce = message => { statusOutput.textContent = message; };
    /* 11DZ_CORE_SCRIPT_START:creative-effects-cursor-trail */
const trailCanvas=document.querySelector('#trailCanvas');const trailContext=trailCanvas.getContext('2d');const trailLength=document.querySelector('#trailLength');const trailSize=document.querySelector('#trailSize');const trailReduced=matchMedia('(prefers-reduced-motion: reduce)').matches;const trailHueOffset=0;let trailPoints=[];let trailCursor={x:0,y:0};let trailFrame=0;function sizeTrailCanvas(){const rect=trailCanvas.getBoundingClientRect();const ratio=Math.min(2,devicePixelRatio||1);trailCanvas.width=Math.max(320,Math.round(rect.width*ratio));trailCanvas.height=Math.max(280,Math.round(rect.height*ratio));trailContext.setTransform(ratio,0,0,ratio,0,0);trailCursor={x:rect.width/2,y:rect.height/2};}function addTrailPoint(x,y){trailCursor={x,y};trailPoints.push({x,y,life:1,hue:(trailHueOffset+trailPoints.length*17)%360});while(trailPoints.length>Number(trailLength.value))trailPoints.shift();cancelAnimationFrame(trailFrame);drawTrail();}function drawTrail(){const rect=trailCanvas.getBoundingClientRect();trailContext.clearRect(0,0,rect.width,rect.height);trailPoints.forEach((point,index)=>{if(!trailReduced)point.life-=.018;const ratio=(index+1)/trailPoints.length;trailContext.beginPath();trailContext.fillStyle='hsla('+point.hue+',90%,68%,'+Math.max(0,point.life*ratio)+')';trailContext.shadowBlur=18;trailContext.shadowColor='hsl('+point.hue+' 90% 68%)';trailContext.arc(point.x,point.y,Number(trailSize.value)*ratio,0,Math.PI*2);trailContext.fill();});trailContext.shadowBlur=0;trailPoints=trailPoints.filter(point=>point.life>0);if(trailPoints.length&&!trailReduced)trailFrame=requestAnimationFrame(drawTrail);}function pointerTrail(event){const rect=trailCanvas.getBoundingClientRect();addTrailPoint(event.clientX-rect.left,event.clientY-rect.top);}trailCanvas.addEventListener('pointermove',pointerTrail);trailCanvas.addEventListener('pointerdown',pointerTrail);trailCanvas.addEventListener('keydown',event=>{if(!['ArrowLeft','ArrowRight','ArrowUp','ArrowDown'].includes(event.key))return;event.preventDefault();const rect=trailCanvas.getBoundingClientRect();trailCursor.x=Math.max(0,Math.min(rect.width,trailCursor.x+(event.key==='ArrowRight'?18:event.key==='ArrowLeft'?-18:0)));trailCursor.y=Math.max(0,Math.min(rect.height,trailCursor.y+(event.key==='ArrowDown'?18:event.key==='ArrowUp'?-18:0)));addTrailPoint(trailCursor.x,trailCursor.y);});trailLength.addEventListener('input',()=>{document.querySelector('#trailLengthValue').textContent=trailLength.value;announce('Light trail length '+trailLength.value);});trailSize.addEventListener('input',()=>{document.querySelector('#trailSizeValue').textContent=trailSize.value+'px';announce('Light Dot Size '+trailSize.value+' pixels');});document.querySelector('#trailClear').addEventListener('click',()=>{trailPoints=[];cancelAnimationFrame(trailFrame);drawTrail();announce('The pointer trail has been cleared');});addEventListener('resize',sizeTrailCanvas,{passive:true});sizeTrailCanvas();
/* 11DZ_CORE_SCRIPT_END */
const workflowBridgeSpec={"name":"creative-effects-cursor-trail","selector":"[data-core-bridge=\"creative-effects-cursor-trail\"]","mode":"range","key":"ArrowRight"};const workflowBridgeCore=document.querySelector('#workflowCore');const workflowBridgeControl=workflowBridgeCore.querySelector(workflowBridgeSpec.selector);if(!workflowBridgeControl)throw new Error('Missing core bridge: '+workflowBridgeSpec.name);const workflowBridgeControlledId=workflowBridgeControl.getAttribute('aria-controls');const workflowBridgeControlled=workflowBridgeControlledId?document.getElementById(workflowBridgeControlledId):null;workflowBridgeCore.dataset.workflowBridge=workflowBridgeSpec.name;workflowBridgeCore.dataset.workflowVersion='0';function workflowBridgeSerialize(){const visualNode=workflowBridgeCore.firstElementChild||workflowBridgeCore;const visual=getComputedStyle(visualNode);const outputs=[...workflowBridgeCore.querySelectorAll('output,[role="status"],[aria-live],meter,progress')].map(node=>node.textContent.trim()+'|'+node.getAttribute('aria-valuenow')+'|'+node.getAttribute('value'));const canvas=[...workflowBridgeCore.querySelectorAll('canvas')].map(node=>{try{return node.toDataURL().slice(-160);}catch{return 'canvas-unavailable';}});return JSON.stringify({html:workflowBridgeCore.innerHTML,status:statusOutput.textContent,outputs,canvas,visual:[visual.display,visual.visibility,visual.opacity,visual.transform,visual.filter,visual.backgroundColor,visual.width,visual.height],scroll:workflowBridgeSpec.mode==='scroll'?workflowBridgeControl.scrollTop:null});}let workflowBridgeLastEffect=workflowBridgeSerialize();function workflowBridgeSummary(){const label=(workflowBridgeControl.getAttribute('aria-label')||workflowBridgeControl.textContent||workflowBridgeControl.value||workflowBridgeSpec.name).trim().replace(/\s+/g,' ').slice(0,72);return label+' · Core Version '+workflowBridgeCore.dataset.workflowVersion;}function workflowBridgeCommit(){const effect=workflowBridgeSerialize();if(effect===workflowBridgeLastEffect)return {changed:false,summary:workflowBridgeSummary()};workflowBridgeLastEffect=effect;workflowBridgeCore.dataset.workflowVersion=String(Number(workflowBridgeCore.dataset.workflowVersion||0)+1);return {changed:true,summary:workflowBridgeSummary()};}function workflowBridgeScheduleCommit(){queueMicrotask(workflowBridgeCommit);setTimeout(workflowBridgeCommit,0);}workflowBridgeControl.addEventListener("input",workflowBridgeScheduleCommit);workflowBridgeControl.addEventListener('change',workflowBridgeScheduleCommit);const workflowBridgeStateTargets=new Set(workflowBridgeCore.querySelectorAll('dialog'));if(workflowBridgeControlled&&workflowBridgeCore.contains(workflowBridgeControlled))workflowBridgeStateTargets.add(workflowBridgeControlled);workflowBridgeStateTargets.forEach(target=>['close','cancel','toggle'].forEach(eventName=>target.addEventListener(eventName,workflowBridgeScheduleCommit)));function workflowBridgeActivate(){workflowBridgeControl.focus();const step=Number(workflowBridgeControl.step)||1;const min=workflowBridgeControl.min===''?-Infinity:Number(workflowBridgeControl.min);const max=workflowBridgeControl.max===''?Infinity:Number(workflowBridgeControl.max);let next=Math.min(max,Math.max(min,(Number(workflowBridgeControl.value)||0)+step));if(next===Number(workflowBridgeControl.value))next=Math.max(min,Number(workflowBridgeControl.value)-step);workflowBridgeControl.value=String(next);workflowBridgeControl.dispatchEvent(new Event('input',{bubbles:true}));workflowBridgeControl.dispatchEvent(new Event('change',{bubbles:true}));const result=workflowBridgeCommit();setTimeout(workflowBridgeCommit,0);return result;}function workflowBridgeReset(){workflowBridgeCore.dataset.workflowVersion='0';workflowBridgeLastEffect=workflowBridgeSerialize();}
const workflowDispatchCore=document.querySelector('#workflowCore');const workflowDispatchForm=document.querySelector('#workflowDispatchForm');const workflowDispatchSelect=document.querySelector('#workflowDispatchActive');const workflowDispatchJobs=new Map([["1",{"label":"Sonar Scan","amount":12,"priority":"normal","touches":0}],["2",{"label":"Hot Spring Sampling","amount":21,"priority":"urgent","touches":0}]]);let workflowDispatchSerial=workflowDispatchJobs.size;function workflowDispatchDrive(job,activate=false){workflowDispatchCore.dataset.dispatchPriority=job.priority;workflowDispatchCore.style.setProperty('--dispatch-load',Math.min(.95,.16+job.amount/120));const bridge=activate?workflowBridgeActivate():{changed:false,summary:workflowBridgeSummary()};document.querySelector('#workflowDispatchSummary').textContent=job.label+' · '+job.amount+' m · '+bridge.summary;announce('Dive mission current task:'+job.label);}function workflowDispatchRender(activeId,activate=false){workflowDispatchSelect.replaceChildren(...[...workflowDispatchJobs].map(([id,job])=>{const option=document.createElement('option');option.value=id;option.textContent=job.label+(job.priority==='urgent'?' · Urgent':'');return option;}));workflowDispatchSelect.value=activeId;workflowDispatchDrive(workflowDispatchJobs.get(activeId),activate);}workflowDispatchForm.addEventListener('submit',event=>{event.preventDefault();const data=new FormData(workflowDispatchForm);const id=String(++workflowDispatchSerial);workflowDispatchJobs.set(id,{label:document.querySelector('#workflowDispatchKind').value,amount:Math.max(1,Number(document.querySelector('#workflowDispatchAmount').value)||1),priority:data.get('workflowPriority'),touches:0});workflowDispatchRender(id,true);});workflowDispatchSelect.addEventListener('change',()=>workflowDispatchDrive(workflowDispatchJobs.get(workflowDispatchSelect.value),true));workflowDispatchCore.addEventListener('focusin',()=>{const job=workflowDispatchJobs.get(workflowDispatchSelect.value);if(!job)return;job.touches+=1;document.querySelector('#workflowDispatchSummary').textContent=job.label+' · '+job.amount+' m · '+workflowBridgeSummary();});workflowDispatchRender('1',false);
workflowBridgeReset();
  </script>
</body>
</html>
Page feedbackFound an issue or have suggestions for improvement?
Feedback features need to be used in the online version

Please enable JavaScript in the online version before submitting; local features of the current tool or example are not affected.

Go to the online version for feedback