This is a continuous version of the purple dots illusion. Keep staring at the crosshairs. Eventually, instead of a white gap rotating, you’ll start seeing a bright band of the opponent color.
Incidentally, you’ll notice that this opponent color is quite bright - it in fact appears brighter than the white background of your display! This is because you’re experiencing a Chimerical color.
I’m sure this has been discovered before, but I came up with this particular version.
Source Code
---title: Ring with rotating gapdate: 2018-09-27categories: [optical-illusion]image: /images/ring-with-rotating-gap.jpg---This is a continuous version of the [purple dotsillusion](./purple-dots.qmd). Keep staring at thecrosshairs. Eventually, instead of a white gap rotating, you'll startseeing a bright band of the opponent color.Incidentally, you'll notice that this opponent color is quite bright -it in fact appears brighter than the white background of your display!This is because you're experiencing a [Chimericalcolor](https://en.wikipedia.org/wiki/Impossible_color).<divid="main"style="height: 600px"></div>```{ojs}import { dom, time } from"/src/cscheid/cscheid.js";{var canvasEl = d3.select("#main").append("canvas").attr("width",600).attr("height",600);var canvasCrosshair = d3.select("#main").append("canvas").attr("width",600).attr("height",600).style("position","relative").style("top","-600px"); d3.select("#main").select("div").style("height","600px");var ctx = dom.setupCanvas(canvasEl.node());var xhCtx = dom.setupCanvas(canvasCrosshair.node());// crosshairs xhCtx.fillStyle="rgba(0,0,0,0)"; xhCtx.fillRect(0,0,600,600); xhCtx.strokeStyle="black"; xhCtx.lineWidth=1* ctx.dpr; xhCtx.beginPath(); xhCtx.moveTo(280,300); xhCtx.lineTo(320,300); xhCtx.moveTo(300,280); xhCtx.lineTo(300,320); xhCtx.stroke(); canvasEl.node().style.filter="blur(10px)";functiontick() {var now = time.elapsed();// wipe ctx.fillStyle="white"; ctx.fillRect(0,0,600,600); ctx.lineWidth=30* ctx.dpr;var nSteps =~~(100*Math.PI*2);var radius =200;var xScale = d3.scaleLinear().domain([-1,1]).range([50,550]);var yScale = d3.scaleLinear().domain([-1,1]).range([550,50]);for (var i=0; i<nSteps;++i) {var angleBeg = (i / nSteps) *Math.PI*2;var angleEnd = ((i+1.5) / nSteps) *Math.PI*2;var lerp = d3.interpolateLab(d3.hcl(angleBeg *180/Math.PI,70,50),"white");var gapCenterAngle = now * (Math.PI*2) % (Math.PI*2);var sigma2 =1;var weight =Math.max(Math.exp(-Math.pow(gapCenterAngle - angleBeg,2) / sigma2),Math.exp(-Math.pow((gapCenterAngle +2*Math.PI) - angleBeg,2) / sigma2),Math.exp(-Math.pow((gapCenterAngle -2*Math.PI) - angleBeg,2) / sigma2), ); ctx.strokeStyle=String(lerp(weight)); ctx.beginPath(); ctx.moveTo(xScale(Math.cos(angleBeg)),yScale(Math.sin(angleBeg))); ctx.lineTo(xScale(Math.cos(angleEnd)),yScale(Math.sin(angleEnd))); ctx.stroke(); }window.requestAnimationFrame(tick); }tick();}```## AcknowledgementsI'm sure this has been discovered before, but I came up with thisparticular version.