VCV Prototype: Share your prototypes

It might be useful to encourage script sharing with VCV Prototype, since it’s easy to share your module prototypes. You’ll first need VCV Prototype to create or load scripts.

How to share patches

There are many ways, such as uploading the script itself as an attachment, but I find this to be the easiest method:

  • Load your script in VCV Prototype.
  • Right click on the panel and select Preset > Copy, or hover your mouse over the panel and use Ctrl-C (Cmd on Mac).
  • Paste this text into this thread as a code block, by surrounding the text by triple backticks (```) on their own line. You may omit the "params" object if you want.

How to load patches

  • Highlight and copy the module preset.
  • Hover over an empty rack space, or an existing VCV Prototype module, and press Ctrl-V (Cmd on Mac).
2 Likes

Factorial router

Inspired from Module to Switch Cable Routes.

This uses input 6 as a CV control to choose the permutation number. The CV is split into 120 intervals, each corresponding to a permutation of inputs 1 through 5.

{
  "plugin": "VCV-Prototype",
  "version": "1.1.0",
  "model": "Prototype",
  "data": {
    "path": "/home/vortico/src/vcv/Rack-v1/plugins/VCV-Prototype/examples/factorial_router.js",
    "script": "\nfunction permutation(arr, k) {\n\tvar n = arr.length\n\tvar factorial = 1\n\tfor (var i = 2; i <= n; i++) {\n\t\tfactorial *= i\n\t}\n\tvar perm = []\n\n\tfor (var i = n; i >= 1; i--) {\n\t\tfactorial /= i\n\t\tvar j = Math.floor(k / factorial)\n\t\tk %= factorial\n\t\tvar el = arr[j]\n\t\tarr.splice(j, 1)\n\t\tperm.push(el)\n\t}\n\n\treturn perm\n}\n\nconfig.bufferSize = 32\n\nfunction process(block) {\n\t// Get factorial index from input 6\n\tvar k = Math.floor(block.inputs[5][0] / 10 * 120)\n\tk = Math.min(Math.max(k, 0), 120 - 1)\n\t// Get index set permutation\n\tvar perm = permutation([0, 1, 2, 3, 4], k)\n\tdisplay(perm)\n\tfor (var i = 0; i < 5; i++) {\n\t\t// Permute input i\n\t\tfor (var j = 0; j < block.bufferSize; j++) {\n\t\t\tblock.outputs[i][j] = block.inputs[perm[i]][j]\n\t\t}\n\t}\n}\n"
  }
}
1 Like

Here is an example file were you could see that the module or the script does not work as it should.Factorial Switch 5!.vcv (9.2 KB)

Output 1 sents nothing, all values seem been shifted by one down,
reading the script for me it seems a mix of values for 6! and 5!?

Knobs 1 - 5 set the values from 1 to 5, knob 6 is used to switch

1 Like

Two indexed buffers intended for use as phase driven loop recorders. Coming soon as my first proper module.

  "id": 3139,
  "plugin": "VCV-Prototype",
  "version": "1.1.0",
  "model": "Prototype",
  "params": [
    {
      "id": 0,
      "value": 0.5
    },
    {
      "id": 1,
      "value": 0.5
    },
    {
      "id": 2,
      "value": 0.5
    },
    {
      "id": 3,
      "value": 0.5
    },
    {
      "id": 4,
      "value": 0.5
    },
    {
      "id": 5,
      "value": 0.5
    },
    {
      "id": 6,
      "value": 0.0
    },
    {
      "id": 7,
      "value": 0.0
    },
    {
      "id": 8,
      "value": 0.0
    },
    {
      "id": 9,
      "value": 0.0
    },
    {
      "id": 10,
      "value": 0.0
    },
    {
      "id": 11,
      "value": 0.0
    }
  ],
  "leftModuleId": 3143,
  "rightModuleId": 3141,
  "data": {
    "path": "/home/ewen/.Rack/PhaseLooper.js",
    "script": "// PhaseLooper.js (C)2019 Ewen Bates\n\n\n\nconfig.frameDivider = 16\nconfig.bufferSize = 1\n\nvar samplesA = new Float32Array(1000)\nvar samplesB = new Float32Array(1000)\n\nvar visuals = [\"+---------\", \"-+--------\", \"--+-------\", \"---+------\", \"----+-----\", \"-----+----\", \"------+---\", \"-------+--\", \"--------+-\", \"---------+\"]\n\nfunction process(args)\n{\n    var phaseA = args.inputs[0][0]\n    var cvA = args.inputs[1][0]\n    var writeA = args.inputs[2][0]\n    var idxA = Math.floor(phaseA * 100)\n\n    var phaseB = args.inputs[3][0]\n    var cvB = args.inputs[4][0]\n    var writeB = args.inputs[5][0]\n    var idxB = Math.floor(phaseB * 100)\n\n    if(writeA) samplesA[idxA] = cvA\n    if(writeB) samplesB[idxB] = cvB\n\n    if(args.switches[2]) zeroArray(samplesA)\n    if(args.switches[5]) zeroArray(samplesB)\n\n    args.outputs[0][0] = samplesA[idxA]\n    args.outputs[1][0] = cvA\n    args.outputs[2][0] = writeA\n    args.outputs[3][0] = samplesB[idxB]\n    args.outputs[4][0] = cvB\n    args.outputs[5][0] = writeB\n\n    args.switchLights[0][1] = phaseA / 10\n    args.switchLights[1][2] = samplesA[idxA] / 10\n    args.switchLights[2][0] = writeA\n\n    args.switchLights[3][1] = phaseB / 10\n    args.switchLights[4][2] = samplesB[idxB] / 10\n    args.switchLights[5][0] = writeB\n\n    display(visuals[Math.floor(phaseA)] + \"  \" + visuals[Math.floor(phaseB)] + \"\\n\" + idxA + \" \" + idxB)\n}\n\nfunction zeroArray(array)\n{\n    for(i = 0; i < 1000; i++) array[i] = 0\n}\n"
  }
}```

Thanks, fixed indexing typo in Factorial Router.

Step sequencer that does random walks/Brownian motions on each step’s value. (My first attempt at a Prototype script so may contain some bugs…)

edit : Updated the code and some documentation :

CV input 1 takes in external clock. (There’s no internal clock yet.)
CV input 2 takes in a pulse that resets the sequencer.

CV output 1 is the step sequencer value.

Knob 1 : Random walk probability distribution parameter 1
Knob 2 : Random walk probability distribution parameter 2
Knob 3 : Random walk probability distribution type :

  1. Hyperbolic cosine (close to a bell/Gaussian distribution), knob 1 controls the mean, knob 2 controls the spread
  2. Cauchy (also bell like, but can make huge jumps even with a very small knob 2 value), knob 1 controls the mean, knob 2 controls the spread
  3. Discrete distribution that always jumps down or up. Knob 1 controls the probability of downward or upward jump. Knob 2 controls the jump size.

Knob 6 : Number of steps played back, between 1 and 16.

Switch button 1 : Resets the sequencer
Switch button 2 : Switches between the sequencer reset types :

  1. All steps at zero
  2. All steps at minimum (-2.0 volts)
  3. All steps at maximum (2.0 volts)
  4. All steps randomized with a uniform random distribution between -2.0 and 2.0 volts
{
  "id": 24,
  "plugin": "VCV-Prototype",
  "version": "1.1.0",
  "model": "Prototype",
  "params": [
    {
      "id": 0,
      "value": 0.5
    },
    {
      "id": 1,
      "value": 0.0924999341
    },
    {
      "id": 2,
      "value": 0.0
    },
    {
      "id": 3,
      "value": 0.5
    },
    {
      "id": 4,
      "value": 0.5
    },
    {
      "id": 5,
      "value": 0.271500021
    },
    {
      "id": 6,
      "value": 0.0
    },
    {
      "id": 7,
      "value": 0.0
    },
    {
      "id": 8,
      "value": 0.0
    },
    {
      "id": 9,
      "value": 0.0
    },
    {
      "id": 10,
      "value": 0.0
    },
    {
      "id": 11,
      "value": 0.0
    }
  ],
  "data": {
    "path": "C:\\MusicAudio\\vcv\\mysequencer1.js",
    "script": "config.frameDivider = 32\nconfig.bufferSize = 1\ncounter = 0\nstepon = false\noutvolt = 0.0\nhasreset = false\nresetmode = 0\nresetmodechanged = false\noutvoltmin = -2.0\noutvoltmax = 2.0\n\nvar steps = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]\n\npi = 3.141592653\n\nfunction hypcosrand(mean,dev)\n{\n\tvar z = Math.random()\n\treturn mean+dev*(2/pi*Math.log(Math.tan(pi/2.0*z)))\n}\n\nfunction cauchyrand(mean,dev)\n{\n\tvar z = Math.random()\n\treturn mean + dev * Math.tan(pi*(z-0.5))\n}\n\nfunction bernouillirand(prob, step)\n{\n\tvar z = Math.random()\n\tif (z>prob)\n\t\treturn -step\n\treturn step\n}\n\nfunction clamp(val,minv,maxv)\n{\n\tif (val<minv)\n\t\treturn minv\n\tif (val>maxv)\n\t\treturn maxv\n\treturn val\n}\n\nfunction reflect(val,minv,maxv)\n{\n\tvar temp = val\n\twhile (temp<minv || temp>maxv)\n\t{\n\t\tif (temp<minv)\n\t\t\ttemp = minv + (minv - temp);\n\t\tif (temp>maxv)\n\t\t\ttemp = maxv + (maxv - temp);\n\t}\n\treturn temp\n}\n\nfunction walkarray(arr,minv,maxv,par1,par2,par3)\n{\n\tfor (var i=0;i<arr.length;++i)\n\t{\n\t\tval = arr[i]\n\t\tif (par3 == 0)\n\t\t{\n\t\t\tval += hypcosrand(-1.0+2.0*par1,par2)\n\t\t}\n\t\tif (par3 == 1)\n\t\t{\n\t\t\tval += cauchyrand(-1.0+2.0*par1,par2)\t\n\t\t}\n\t\tif (par3 == 2)\n\t\t{\n\t\t\tval += bernouillirand(par1,par2)\n\t\t}\n\t\tarr[i] = reflect(val,minv,maxv)\n\t}\n}\n\nfunction reset_steps(arr,mode)\n{\n\tfor (var i=0;i<arr.length;++i)\n\t{\n\t\tif (mode == 0)\n\t\t\tarr[i] = 0.0\n\t\tif (mode == 1)\n\t\t\tarr[i] = outvoltmin\n\t\tif (mode == 2)\n\t\t\tarr[i] = outvoltmax\n\t\tif (mode == 3)\n\t\t\tarr[i] = outvoltmin+(outvoltmax-outvoltmin)*Math.random()\n\t}\n}\n\nfunction process(block)\n{\n\tif (block.inputs[0][0]>0.5 && stepon==false) // step clock\n\t{\n\t\toutvolt = steps[counter]\n\t\tcounter++\n\t\tif (counter>=1.0+15.0*block.knobs[5])\n\t\t{\n\t\t\tcounter = 0\n\t\t\twalkarray(steps,outvoltmin,outvoltmax,block.knobs[0],block.knobs[1],Math.round(block.knobs[2]*2))\n\t\t}\n\t\tstepon = true\n\t}\n\tif (block.inputs[0][0]<0.5 && stepon==true)\n\t{\n\t\tstepon = false\n\t}\n\tif (block.inputs[1][0]>0.5 || block.switches[0]>0.5) // reset\n\t{\n\t\treset_steps(steps,resetmode)\n\t}\n\tif (block.switches[1]>0.5 && resetmodechanged == false)\n\t{\n\t\tresetmode = (resetmode+1) % 4\n\t\tresetmodechanged = true\n\t}\n\tif (block.switches[1]<0.5 && resetmodechanged == true)\n\t{\n\t\tresetmodechanged = false\n\t}\n\tdisplay(resetmode)\n\tblock.outputs[0][0]=outvolt\n}\n"
  }
}
2 Likes

Chord

Converts a single input on Input 0 to either a major or minor chord on Outputs 0, 1, 2

Pressing Button 1 changes from major to minor.

Use Merge to convert the 3 outputs into a single poly cable, or use 3 oscillators, your choice!

{
  "id": 156,
  "plugin": "VCV-Prototype",
  "version": "1.1.1",
  "model": "Prototype",
  "params": [
    {
      "id": 0,
      "value": 0.5
    },
    {
      "id": 1,
      "value": 0.5
    },
    {
      "id": 2,
      "value": 0.5
    },
    {
      "id": 3,
      "value": 0.5
    },
    {
      "id": 4,
      "value": 0.5
    },
    {
      "id": 5,
      "value": 0.5
    },
    {
      "id": 6,
      "value": 0.0
    },
    {
      "id": 7,
      "value": 0.0
    },
    {
      "id": 8,
      "value": 0.0
    },
    {
      "id": 9,
      "value": 0.0
    },
    {
      "id": 10,
      "value": 0.0
    },
    {
      "id": 11,
      "value": 0.0
    }
  ],
  "leftModuleId": 155,
  "data": {
    "path": "/Users/jerry/Documents/Rack/plugins-v1/VCV-Prototype/examples/chord.js",
    "script": "config.frameDivider = 32;\nconfig.bufferSize = 1;\n\nconst step = 1.0 / 12.0;\nconst MAJOR = 4;\nconst MINOR = 3;\n\n\ndisplay('Chord');\n\n// CV module, based on SynthDevKit CV module\nfunction CV(threshold) {\n  this.threshold = threshold;\n  this.reset();\n}\n\nCV.prototype.update = function update(current) {\n  this.lastValue = current;\n\n  this.triggerIntervalCount++;\n\n  if (current >= this.threshold) {\n    if (this.triggered === false) {\n      this.triggered = true;\n\n      this.triggerCount++;\n\n      this.lastTriggerInterval = this.triggerIntervalCount;\n\n      this.triggerIntervalCount = 0;\n    }\n  } else {\n    this.triggered = false;\n  }\n};\n\nCV.prototype.newTrigger = function newTrigger() {\n  if (this.triggered === true && this.lastTriggered === false) {\n    this.lastTriggered = true;\n\n    return true;\n  }\n\n  this.lastTriggered = this.triggered;\n\n  return false;\n};\n\nCV.prototype.reset = function reset() {\n  this.triggered = false;\n  this.lastTriggered = false;\n  this.lastValue = 0;\n  this.triggerCount = 0;\n  this.triggerIntervalCount = 0;\n  this.lastTriggerInterval = 0;\n};\n\nCV.prototype.currentValue = function() {\n  return this.lastValue;\n};\n\nCV.prototype.isLow = function isLow() {\n  return !this.triggered;\n};\n\nCV.prototype.isHigh = function isHigh() {\n  return this.triggered;\n};\n\nCV.prototype.triggerInterval = function() {\n  return this.lastTriggerInterval;\n};\n\nCV.prototype.triggerTotal = function() {\n  return this.triggerCount;\n};\n\nlet cvState = new CV(1);\nlet switchValue = 0;\n\nfunction process(block) {\n  let voct = block.inputs[0][0];\n\n  cvState.update(block.switches[0]);\n  if (cvState.newTrigger()) {\n    switchValue = !switchValue;\n  }\n\n  var middle = switchValue ? MINOR : MAJOR;\n\n  display('Chorus - ' + (switchValue ? 'Minor' : 'Major'));\n\n  block.outputs[0][0] = voct;\n  block.outputs[1][0] = voct + (step * middle);\n  block.outputs[2][0] = voct + (step * 7);\n}\n"
  }
}
1 Like

experimenting with Math.min and Math.max to make a comparator:
Input 1 = comparator in1
input 2 = comparator in2
input 3 = biasCV
knob 1 = bias
output 1 = max
output 2 = min
output 3 = mix (influenced by bias)

{
  "plugin": "VCV-Prototype",
  "version": "1.1.1",
  "model": "Prototype",
  "data": {
    "path": "~/logic.js",
    "script": "config.frameDivider = 1\nconfig.bufferSize = 16\n\nvar phase = 0\nvar amp = 1\nvar mix;\nvar bias_cv\nlet clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));\n\n\nfunction process(block) {\n    // block.knobs[0] = 0.5\n\t// Knob ranges from -5 to 5 octaves\n\t// var pitch = block.knobs[0] * 10 - 5\n\t// Input follows 1V/oct standard\n    // pitch += block.inputs[0][0]\n    \n    amp = block.inputs[1][0]\n\n    compare = [block.inputs[0][0], block.inputs[1][0]]\n    max = Math.max(...compare)\n    min = Math.min(...compare)\n\n    // let i = compare.indexOf(Math.max(...compare));\n\n    function indexOfMax(arr) {\n        if (arr.length === 0) {\n            return -1;\n        }\n        var max = arr[0];\n        var maxIndex = 0;\n        for (var i = 1; i < arr.length; i++) {\n            if (arr[i] > max) {\n                maxIndex = i;\n                max = arr[i];\n            }\n        }\n        return maxIndex;\n    }\n\n    var bias = block.knobs[0] * 2 - 1\n    // bias_cv += bias * Math.pow(2, block.inputs[2][0]);\n    if(bias < 0.){\n        mix = clampNumber( ((Math.abs(bias) * min) + (bias * max) * max * block.inputs[2][0]), -10., 10.)\n    } else if (bias > 0.){\n        mix = clampNumber( ((bias * max) + ((bias * -1) * min) * min * block.inputs[2][0]), -10., 10.)\n    }\n\n    var freq = 1\n\t//display(\"Freq: \" + freq.toFixed(3) + \" Hz\")\n\n\t// Set all samples in output buffer\n\tvar deltaPhase = config.frameDivider * block.sampleTime * freq\n\tfor (var i = 0; i < block.bufferSize; i++) {\n\t\t// Accumulate phase\n\t\tphase += deltaPhase\n\t\t// Wrap phase around range [0, 1]\n        phase %= 1\n        // display([bias, bias_cv, mix])\n        \n\t\t// Convert phase to sine output\n        // block.outputs[0][i] = Math.sin(2 * Math.PI * phase) * 5 * amp\n        block.outputs[0][i] = max\n        block.outputs[1][i] = min\n        block.outputs[2][i] = mix\n\t}\n}\n"
  }
}