I took my son to his first drum lesson today. It was awesome. His rock and roll career is fated.
At some point the teacher pulled up a PDF on a screen and was guiding my son through some simple patterns. I noticed that it was kind of hard to see a moving cursor on a white screen.
This happens to me too when I’m on a conference call and sharing my screen. All I want to do is point out stuff on the screen and it’s sometimes hard to get people to look where I want them to look.
So instead of paying close attention to what my son was learning and how to support him in his practicing, I started daydreaming about how to fix this problem.
There are tools to highlight your mouse that I can install on my Mac, and there are extensions I can install in Chrome too. But that seems like overkill. I don’t wanna drain my precious RAM with an ever-present system doohickey. I don’t want yet another browser extension hanging out all the time and bloating my browser’s RAM footprint either. Inefficiency!
A bookmarklet can do this, so I made one. When you click the bookmarklet, it draws a circle around your mouse pointer. When you click it again, the circle goes away.
To install the bookmarklet:
- Easiest method:
- Drag this link to your bookmarks bar in Chrome
- Rename the bookmark something cool.
- Alternate method:
- Copy the code below.
- Right-click your bookmark bar and choose Add Page…
- Give it a name like “pointer”
- Paste the code in the URL field.
- Click Save.
Here are the instructions for using it:
- Click the bookmarklet to turn on the circle, click it again to turn it off.
- Press CTRL+6+7+8 to activate a little tail if you need something even more obvious. (Press that keycombo again to turn it off)
Bookmarklet Code:
javascript:(function(){if(window.cursorHighlighter){window.cursorHighlighter.remove();window.cursorHighlighter=null;window.cursorHighlighterCanvas?.remove();window.cursorHighlighterCanvas=null;cancelAnimationFrame(window.cursorHighlighterAnim);document.removeEventListener('mousemove',window.cursorHighlighterMove);document.removeEventListener('click',window.cursorHighlighterClick);document.removeEventListener('keydown',window.cursorHighlighterKey);window.cursorHighlighterKeySeq=null;window.cursorHighlighterParticles=null;return;}const ball=document.createElement('div');ball.id='cursor-highlighter';ball.style.cssText='position:fixed;width:36px;height:36px;border-radius:50%;pointer-events:none;z-index:2147483647;transition:transform 0.15s ease,box-shadow 0.15s ease;mix-blend-mode:difference;border:2px solid;';document.body.appendChild(ball);const canvas=document.createElement('canvas');canvas.style.cssText='position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:2147483646;display:none;';canvas.width=window.innerWidth;canvas.height=window.innerHeight;document.body.appendChild(canvas);const ctx=canvas.getContext('2d');let currentColor='#00FFFF';let tailEnabled=false;let cursor={x:0,y:0};let particles=[];const particleCount=25;const trailSpeed=0.4;function getContrastColor(x,y){const el=document.elementFromPoint(x,y);if(!el)return'#00FFFF';const bg=window.getComputedStyle(el).backgroundColor;const match=bg.match(/\d+/g);if(!match||match.length<3)return'#00FFFF';const r=parseInt(match[0]),g=parseInt(match[1]),b=parseInt(match[2]);const luminance=0.299*r+0.587*g+0.114*b;return luminance>128?'#FF00FF':'#00FFFF';}function hexToRgb(hex){const r=parseInt(hex.slice(1,3),16);const g=parseInt(hex.slice(3,5),16);const b=parseInt(hex.slice(5,7),16);return{r,g,b};}function updateParticles(){if(!tailEnabled)return;ctx.clearRect(0,0,canvas.width,canvas.height);let x=cursor.x;let y=cursor.y;particles.forEach((particle,index)=>{const nextParticle=particles[index+1]||particles[0];particle.x=x;particle.y=y;x+=(nextParticle.x-particle.x)*trailSpeed;y+=(nextParticle.y-particle.y)*trailSpeed;});ctx.lineCap='round';ctx.lineJoin='round';const rgb=hexToRgb(currentColor);for(let i=1;i<particles.length;i++){const progress=i/particles.length;const alpha=1-progress;const width=15*(1-progress);ctx.strokeStyle=`rgba(${rgb.r},${rgb.g},${rgb.b},${alpha})`;ctx.lineWidth=width;ctx.beginPath();ctx.moveTo(particles[i-1].x,particles[i-1].y);ctx.lineTo(particles[i].x,particles[i].y);ctx.stroke();}}function animLoop(){updateParticles();window.cursorHighlighterAnim=requestAnimationFrame(animLoop);}window.cursorHighlighterMove=function(e){const color=getContrastColor(e.clientX,e.clientY);ball.style.left=(e.clientX-18)+'px';ball.style.top=(e.clientY-18)+'px';ball.style.backgroundColor=color;ball.style.borderColor=color;ball.style.boxShadow='none';currentColor=color;cursor.x=e.clientX;cursor.y=e.clientY;if(tailEnabled&&particles.length===0){for(let i=0;i<particleCount;i++){particles.push({x:cursor.x,y:cursor.y});}}};window.cursorHighlighterClick=function(e){ball.style.transform='scale(0.7)';ball.style.boxShadow=`0 0 20px ${currentColor}, 0 0 40px ${currentColor}, 0 0 60px ${currentColor}`;setTimeout(()=>{ball.style.transform='scale(1)';ball.style.boxShadow='none';},150);};let keySeq='';let keyTimer;window.cursorHighlighterKey=function(e){if(e.ctrlKey){keySeq+=e.key;clearTimeout(keyTimer);keyTimer=setTimeout(()=>keySeq='',1000);if(keySeq.includes('678')){tailEnabled=!tailEnabled;canvas.style.display=tailEnabled?'block':'none';if(tailEnabled){particles=[];for(let i=0;i<particleCount;i++){particles.push({x:cursor.x,y:cursor.y});}}else{particles=[];ctx.clearRect(0,0,canvas.width,canvas.height);}keySeq='';}}};window.addEventListener('resize',()=>{canvas.width=window.innerWidth;canvas.height=window.innerHeight;});document.addEventListener('mousemove',window.cursorHighlighterMove);document.addEventListener('click',window.cursorHighlighterClick);document.addEventListener('keydown',window.cursorHighlighterKey);animLoop();window.cursorHighlighter=ball;window.cursorHighlighterCanvas=canvas;window.cursorHighlighterParticles=particles;})();