JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.9/pixi.min.js"></script>

CSS

body {
  margin: 0;
  padding: 0;
  background-color: #CCCCCC;
}

JavaScript

var renderer = PIXI.autoDetectRenderer(500, 500, {antialias: true});
document.body.appendChild(renderer.view);

var dragging = false;

// create the root of the scene graph
var stage = new PIXI.Container();
stage.interactive = true;
stage.buttonMode = true;
stage
  .on('mousedown', onDragStart)
  .on('touchstart', onDragStart)
  .on('mouseup', onDragEnd)
  .on('touchend', onDragEnd)
  .on('mousemove', onMouseMove)
  .on('touchmove', onMouseMove);


// background - image to expose
var bg = PIXI.Sprite.fromImage('https://placeholdit.imgix.net/~text?txtsize=40&txt=TEST_IMAGE&w=500&h=500');
bg.anchor.set(0.5);
bg.position.set(250, 250);
stage.addChild(bg);

// mask
var mask = new PIXI.Graphics();
mask.position.set(250, 250);
mask.beginFill(0x000000, 1);
mask.drawRect(0, 0, 0, 0);
mask.endFill();

bg.mask = mask;
stage.addChild(mask);

// cursor
var cursorCircle = new PIXI.Graphics();
cursorCircle.visible = false;
cursorCircle.lineStyle(2, 0xFFCC00, 1);
cursorCircle.beginFill(0xFFCC00);
cursorCircle.drawCircle(0, 0, 25);
cursorCircle.endFill();
stage.addChild(cursorCircle);


function onMouseMove(event) {
  // move cursor
  cursorCircle.visible = true;
  cursorCircle.position.x = event.data.global.x;
  cursorCircle.position.y = event.data.global.y;

  // expose image under mask
  if (dragging) {
    var pos = event.data.getLocalPosition(mask);
    mask.beginFill(0x000000, 0);
    mask.lineStyle(0);
    mask.drawCircle(pos.x, pos.y, 25);
    mask.endFill();
  }
}

function onDragStart(event) {
  dragging = true;
}

function onDragEnd(event) {
  dragging = false
}


// start animating
animate();

function animate() {
  requestAnimationFrame(animate);

  // render the container
  renderer.render(stage);
}