// ==UserScript== // @name Xilly - Mouse Trail // @namespace http://experimenting.website/ // @version 0.1 // @description Add firefly mouse trail // @author EXPERIMENTING // @match https://x.com/* // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; // ----- CONFIG ----- const MAX_PARTICLES = 10; const SPAWN_RATE = 0.4; const BASE_SIZE = 1.0; const SIZE_VARIATION = 2.2; const LIFE_MIN = 700; const LIFE_MAX = 1600; const SPEED_FACTOR = 0.02; const TAIL_SMOOTH = 0.85; const FRAME_LIMIT_MS = 16.67; const IGNORE_INPUTS = true; const COLORS = [ '255,210,120', '255,200,90', '255,170,70', '255,230,160' ]; // ----- CREATE CANVAS OVERLAY ----- let canvas, ctx; function createCanvas() { canvas = document.createElement('canvas'); canvas.style.position = 'fixed'; canvas.style.left = '0'; canvas.style.top = '0'; canvas.style.width = '100%'; canvas.style.height = '100%'; canvas.style.pointerEvents = 'none'; canvas.style.zIndex = '2147483646'; canvas.id = 'firefly-trail-canvas'; document.documentElement.appendChild(canvas); ctx = canvas.getContext('2d', { alpha: true }); resizeCanvas(); window.addEventListener('resize', resizeCanvas, { passive: true }); } function resizeCanvas() { if (!canvas) return; const dpr = Math.max(1, window.devicePixelRatio || 1); const w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); const h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); canvas.width = Math.floor(w * dpr); canvas.height = Math.floor(h * dpr); canvas.style.width = w + 'px'; canvas.style.height = h + 'px'; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } // ----- PARTICLE SYSTEM ----- class Particle { constructor() { this.reset(0, 0); } reset(x, y) { this.x = x; this.y = y; this.vx = (Math.random() - 0.5) * 0.3; this.vy = (Math.random() - 0.5) * 0.3; this.size = BASE_SIZE + Math.random() * SIZE_VARIATION; this.life = 0; this.maxLife = LIFE_MIN + Math.random() * (LIFE_MAX - LIFE_MIN); this.hue = COLORS[Math.floor(Math.random() * COLORS.length)]; this.alpha = 1; this.alive = true; this.age = 0; } update(dt) { this.vx *= TAIL_SMOOTH; this.vy *= TAIL_SMOOTH; this.x += this.vx * dt; this.y += this.vy * dt; this.age += dt; this.life = this.age; this.alpha = Math.max(0, 1 - this.life / this.maxLife); if (this.life >= this.maxLife) this.alive = false; } draw(ctx) { const g = ctx; const s = this.size; g.save(); // additive blending for glow g.globalCompositeOperation = 'lighter'; // radial gradient for soft glow const grad = g.createRadialGradient(this.x, this.y, 0, this.x, this.y, s * 8); const rgba = (a) => `rgba(${this.hue},${a})`; grad.addColorStop(0, rgba(0.95 * this.alpha)); grad.addColorStop(0.2, rgba(0.6 * this.alpha)); grad.addColorStop(0.5, rgba(0.18 * this.alpha)); grad.addColorStop(1, rgba(0)); g.fillStyle = grad; g.beginPath(); g.arc(this.x, this.y, s * 8, 0, Math.PI * 2); g.fill(); // small core g.globalCompositeOperation = 'source-over'; g.shadowColor = `rgba(${this.hue}, ${0.9 * this.alpha})`; g.shadowBlur = Math.max(6, s * 6); g.fillStyle = `rgba(${this.hue}, ${1 * this.alpha})`; g.beginPath(); g.arc(this.x, this.y, s, 0, Math.PI * 2); g.fill(); g.restore(); } } let particles = []; let freeList = []; function createParticle(x, y) { let p; if (freeList.length) { p = freeList.pop(); p.reset(x, y); } else if (particles.length < MAX_PARTICLES) { p = new Particle(); p.reset(x, y); particles.push(p); } else { // recycle the oldest or dead one for (let i = 0; i < particles.length; i++) { if (!particles[i].alive) { p = particles[i]; p.reset(x, y); break; } } if (!p) { // fallback: overwrite random p = particles[Math.floor(Math.random() * particles.length)]; p.reset(x, y); } } return p; } // ----- MOUSE HANDLING ----- let lastX = 0, lastY = 0, lastTime = performance.now(); let enabled = true; function onMove(e) { if (!enabled) return; if (IGNORE_INPUTS) { const t = e.target; if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable)) return; } const x = e.clientX; const y = e.clientY; const now = performance.now(); const dt = Math.max(1, now - lastTime); const dx = x - lastX; const dy = y - lastY; const dist = Math.sqrt(dx * dx + dy * dy) || 0.001; let toSpawn = Math.min(6, Math.ceil(dist * SPAWN_RATE)); for (let i = 0; i < toSpawn; i++) { const t = i / Math.max(1, toSpawn); const sx = lastX + dx * t + (Math.random() - 0.5) * 2; const sy = lastY + dy * t + (Math.random() - 0.5) * 2; const p = createParticle(sx, sy); p.vx += dx * SPEED_FACTOR * (0.6 + Math.random() * 0.9); p.vy += dy * SPEED_FACTOR * (0.6 + Math.random() * 0.9) + (Math.random() - 0.5) * 0.8; } lastX = x; lastY = y; lastTime = now; } function onTouchMove(e) { if (!e.touches || e.touches.length === 0) return; const t = e.touches[0]; onMove({ clientX: t.clientX, clientY: t.clientY, target: t.target }); } // ----- RENDER LOOP ----- let rafId = null; function frame(now) { if (!ctx) return; ctx.clearRect(0, 0, canvas.width, canvas.height); const dt = 16; for (let i = 0; i < particles.length; i++) { const p = particles[i]; if (!p.alive) continue; p.update(dt); p.draw(ctx); } for (let i = 0; i < particles.length; i++) { if (!particles[i].alive) { freeList.push(particles[i]); } } rafId = requestAnimationFrame(frame); } // ----- ENABLE / DISABLE CONTROL ----- function enable() { if (enabled) return; enabled = true; canvas.style.display = ''; window.addEventListener('mousemove', onMove, { passive: true }); window.addEventListener('touchmove', onTouchMove, { passive: true }); if (!rafId) rafId = requestAnimationFrame(frame); } function disable() { if (!enabled) return; enabled = false; canvas.style.display = 'none'; window.removeEventListener('mousemove', onMove); window.removeEventListener('touchmove', onTouchMove); if (rafId) { cancelAnimationFrame(rafId); rafId = null; } } // toggle with Ctrl+Shift+K window.addEventListener('keydown', (e) => { if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'k') { if (enabled) { disable(); flashStatus('Mouse trail: OFF'); } else { enable(); flashStatus('Mouse trail: ON'); } } }); // small on-screen hint that auto-hides let hintEl; function flashStatus(text) { if (!hintEl) { hintEl = document.createElement('div'); hintEl.style.position = 'fixed'; hintEl.style.right = '18px'; hintEl.style.bottom = '18px'; hintEl.style.padding = '8px 12px'; hintEl.style.fontSize = '13px'; hintEl.style.background = 'rgba(20,20,20,0.7)'; hintEl.style.color = '#fff'; hintEl.style.borderRadius = '8px'; hintEl.style.zIndex = '2147483647'; hintEl.style.pointerEvents = 'none'; document.documentElement.appendChild(hintEl); } hintEl.textContent = text; hintEl.style.opacity = '1'; setTimeout(() => { if (hintEl) hintEl.style.transition = 'opacity 700ms'; if (hintEl) hintEl.style.opacity = '0'; }, 900); } // ----- INIT ----- function init() { // avoid running inside some embedded frames or if canvas already present try { if (window.top !== window.self) return; } catch (e) {} if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', setup, { once: true }); } else { setup(); } } function setup() { createCanvas(); // initial mouse pos center lastX = (window.innerWidth || document.documentElement.clientWidth) / 2; lastY = (window.innerHeight || document.documentElement.clientHeight) / 2; lastTime = performance.now(); window.addEventListener('mousemove', onMove, { passive: true }); window.addEventListener('touchmove', onTouchMove, { passive: true }); rafId = requestAnimationFrame(frame); // initial simple hint flashStatus('Firefly trail enabled — Ctrl+Shift+K to toggle'); } init(); })();