JSFiddle - React, Tailwind, and code Playground

HTML

Volume: <input type='range' min='0' max='100' value='0' oninput=" setVolume(this.value)" ontouchstart="start();" onmousedown="start();">

JavaScript

var min_freq = 1;
var max_freq = 60000;
var steps_per_loop = 24;
var seconds_per_loop = 1;

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var gainNode = audioCtx.createGain();
gainNode.connect(audioCtx.destination);

setVolume(0); // Initialize volume to match range input
var playing = false;

var step_speed = 1000 * seconds_per_loop / steps_per_loop;
var multiplier = Math.pow(2, 1/steps_per_loop)
var current_step = 0;
var oscillators = [];

function shepardLoop () {
    base_freq = min_freq;
    for(i = 0; base_freq < max_freq; i++) {
        if(oscillators[i]) oscillators[i].stop(0);
        freq = base_freq * Math.pow(multiplier, current_step);
        oscillator = audioCtx.createOscillator();
        oscillator.frequency.value = freq; // value in hertz
        oscillator.connect(gainNode);
        oscillator.start(0);
        oscillators[i] = oscillator;
        base_freq = base_freq;
    }
    current_step = (current_step + 1) % steps_per_loop;
    setTimeout(shepardLoop, step_speed);
}

function start() {
    if(!playing) {
        playing = true;
        shepardLoop();
    }
}
function setVolume(volume) {
    gainNode.gain.value = volume / 100 / 12;
}