01 // EFFECT PLUGIN

MASKERAD-8 BITCRUSHER

A simple lo-fi downsampler + bitcrusher with an 8-bit GUI. Crush bits (16→1), hold samples (x1→x32), add drive, filter the top off. Runs 100% in your browser — no upload.

  • BIT DEPTH 1–16
  • DOWNSAMPLE x1–x32
  • 8 PRESET CARTRIDGES
  • VST3 CODE INCLUDED

MASKERAD-8 // BITCRUSHER V1.0

○ STANDBY○ BYPASS
LVL
BIT 16DS x1~48000 HZMIX 0%

* press POWER or PLAY DEMO to boot the audio engine. sound stays on your device.

01 BIT DEPTH8
02 DOWNSAMPLE4x
03 DRIVE2
04 MIX100%
05 TONE (LP)12000HZ
06 VOLUME80%

▼ PRESET CARTRIDGE — TAP TO LOAD

IN: NONE (IDLE)DSP: Tanh(DRV) → HOLD xN → Q(BIT) → LP → OUT

01 — Play

Hit PLAY DEMO for a chiptune loop, LOAD FILE for your own sample, or USE MIC for live crush.

02 — Crush

Lower BIT for grit, raise DOWNSAMPLE for aliasing, push DRIVE for saturation, roll TONE to dull.

03 — Use it

POWER = true bypass. Below is the same DSP as JUCE C++ — paste into a JUCE project to build a real .vst3.

▸ Real VST3? Copy the JUCE C++ DSP
// MASKERAD-8 — minimal JUCE bitcrusher (drop into a JUCE AudioProcessor)
// Params: bitDepth (1..16), downsample (1..32), drive (1..10), mix (0..1)
class Maskerad8DSP {
public:
  void prepare(double sr) { sampleRate = sr; reset(); }
  void reset() { holdL = holdR = 0.0f; counter = 0; }
  void process(juce::AudioBuffer<float>& buf,
               float bits, int ds, float drive, float mix) {
    const int N = std::max(1, ds);
    const float levels = std::pow(2.0f, juce::jlimit(1.0f, 16.0f, bits) - 1.0f);
    auto crush = [&](float x) {
      float d = std::tanh(x * drive);          // drive / saturation
      if (bits >= 16.0f) return d;
      return juce::jlimit(-1.0f, 1.0f,
        std::round(d * levels) / levels);      // bit quantize
    };
    for (int ch = 0; ch < buf.getNumChannels(); ++ch) {
      auto* data = buf.getWritePointer(ch);
      for (int i = 0; i < buf.getNumSamples(); ++i) {
        float dry = data[i];
        if (ch == 0 ? (counter % N == 0) : false) holdL = crush(dry);
        if (ch == 1 ? (counter % N == 0) : false) holdR = crush(dry);
        if (buf.getNumChannels() == 1 && counter % N == 0) holdL = crush(dry);
        float wet = (ch == 0 ? holdL : holdR);
        data[i] = dry * (1.0f - mix) + wet * mix;  // downsample via hold
        if (ch == 0) counter++;
      }
    }
  }
private:
  double sampleRate = 48000.0;
  float holdL = 0, holdR = 0;
  int counter = 0;
};