Opto Theremin

This instrument is a simple optical version of the classical Theremin that was developed for didactic purpose. The instrument also works as a MIDI controller.

This project was presented for a workshop in Berlin, where musicians were interested on how to create a MIDI controller with their own sensors.

We will explain the developed project following the dart-methodology in 3 steps: mechanic, electronic and control.



This low-cost theremin was implemented using a small cardboard box as the shell for the electronics. We make two big holes in the box to include a LDR sensor, and a speaker.

The LDR sensor (see figure) will take charge of control the pitch of the synthesized sensor. Naturally, the sensor captures changes in light from everywhere.


But our intention is to capture change in light made by the movement of the hand just in front of the sensor. For solving this issue, we use a mechanic design solution putting the sensor at the end of a large tube (see figure).

At the same time, the design include illumination with a set of white LEDS the front of the other end of the tube. The goal of this lights is to illuminate the hand that is just in front of the hole of the tube. This way, putting the hand close to the tube where the sensor is, will increase the amount of light the sensor will capture, and moving away the hand will decrease the light captured by the sensor.


In addition to the sensor and the speaker, the system has more elements. The list of the main elements that have to be connected is as follows:

  • Arduino UNO, for the control system.* Power Switch, for turning on/off the theremin.
  • Power plug, for powering the theremin.
  • LDR sensor, for controlling the pitch of the synthesized sound.
  • Potentiometer, in case we want to control another parameter of the instrument.
  • 3 white LEDS, for illuminating outside the tube where the LDR is.

The LDR sensor is connected with a resistor making a voltage divider as explained in how to callibrate a variable resistor sensor. The sensor is connected in one of the analog arduino inputs.

The potentiometer is connected as explained in the potentiometer arduino tutorial, in other of the analog arduino inputs.

The 3 white LEDs are connected in parallel (it is also possible to connect them in series, but you will need up to 9.6V for powering them up)

For calculate the resistor value you need to put in series with the LEDs take into account the following general data about the consume of each led:

  • Red LED : 2V 15mA
  • Green LED: 2.1V 20mA
  • Blue LED : 3.2V 25mA
  • While LED: 3.2V 25mA

Therefore, if you power the LEDs with 5V (that is the common voltage for powering arduino), the consume of 3 white LEDs has to be below 25mA each, that is, 65mA maximum.

\begin{align} R = \frac{5V - 3,2V}{25mA} = 27.69 Ohms \end{align}

The closest available standard resistor value is 33 Ohms, what will give a little less current consumption.



The control is made using Arduino UNO. The control algorithm is quite simple. First we read the sensors values. Then we callibrate this values and map them to be linearly transformed inside the range of minimum and maximum tone pitch. Finally we synthesize the tone with the calculated pitch.

This algorithm is implemented in the following CODE:

 @brief Opto thermin, is a sine audio signal modulated in freccuency 
by a LDR as an optical sensor.

#include <Tone.h>

#define POT_PIN  0
#define LDR_PIN  1
#define SPEAKER_PIN  6
#define MAX_PITCH  2000
#define MIN_PITCH  50

// this data is obtianed empirically in a previous stage of measuring 
// the max and min capture values
#define LDR_MAX  380    
#define LDR_MIN  10

Tone theremin;    // Tone class implements the synthesis of an audio tone.

void setup(){
  theremin.begin( SPEAKER_PIN );    // Specifies where the speaker is connected.
  pinMode( 11, OUTPUT );
  pinMode( LIGHT_CONTROL_PIN, OUTPUT );    // where the LEDs are connected
  analogWrite( LIGHT_CONTROL_PIN, 255 );     // Turn on the lights at the maximum value

  theremin.play( 440 );        // this beep shows that the system is ready... in 1sec

int v_pot = 0;
unsigned long volume = 0;
unsigned long pitch = 0;

int ldr_min = LDR_MIN;

void loop() {

  pitch = analogRead( LDR_PIN );

// The following code just adjust the max and min of the LDR values 
// to the max and min pitch.
// It would be also possible to implement this using map function.

  int A = (MAX_PITCH - MIN_PITCH);
  A = A / (LDR_MAX - LDR_MIN);
  int B = MIN_PITCH * LDR_MAX;
  B = B - (MAX_PITCH - LDR_MIN);
  B = B / (LDR_MAX - LDR_MIN);

  pitch = A * pitch;
  pitch = pitch - B;

  theremin.play( pitch );


The optical theremin can be also used for being a MIDI controller. In that case, instead of transforming the LDR values in a tone variation, we will send Control Change data.

The following example is conceived to control a Kaoss Padd v.3.

#define CONTROL_CHANGE  0xB0
#define PAD_X  12
#define PAD_Y  13
#define TOUCH_PAD  92
#define HOLD  95
#define BPM  0xC0
#define NOTE_ON  0x90
#define NOTE_OFF  0x80

#define POT_PIN  0
#define LDR_PIN  1
#define SPEAKER_PIN  6
#define MODE_PIN  8

#define VALUE_MAX  127  // maximum value for a Control Change MIDI message.
#define VALUE_MIN  0
#define LDR_MAX  380
#define LDR_MIN  10

void setup() {
  //  Set MIDI baud rate:
  pinMode( MODE_PIN, INPUT );
  analogWrite( LIGHT_CONTROL_PIN, 255 );     // [0, 255]

unsigned long v_pot = 0;
unsigned long v_pot_old = 0;
unsigned long v_ldr = 0;
unsigned long v_ldr_old = 0;
void loop() {
  v_pot = analogRead( POT_PIN );  // [0, 1023]
  v_ldr = analogRead( LDR_PIN );  // [0, 1023]

  double A = (VALUE_MAX - VALUE_MIN);
  A = A / (LDR_MAX - LDR_MIN);
  double B = VALUE_MIN * LDR_MAX;
  B = B - (VALUE_MAX * LDR_MIN);
  B = B / (LDR_MAX - LDR_MIN);

  v_pot = A * v_pot + B;
  v_ldr = A * v_ldr + B;

// This 'if' is just a filter to avoid noise in the potentiometer and in the LDR values
  if( (v_pot - v_pot_old) > 8 || ( v_ldr - v_ldr_old ) > 8 ) {
// First of all, we send a MIDI message to inform the KP3 that the pad has been touched   
    Serial.write( TOUCH_PAD );  
// Indicates the the MIDI message is a Control Change
    Serial.write( CONTROL_CHANGE );
// Indicates that the control that is going to be changed is the mobement of PAD in vertical  
    Serial.write( PAD_X );
// Gives the vertical value with the potentiomenter
    Serial.write( v_pot );        
    Serial.write( CONTROL_CHANGE );
    Serial.write( PAD_Y );
    Serial.write( v_ldr );
    Serial.write( TOUCH_PAD );
  v_pot_old = v_pot;
  v_ldr_old = v_ldr; 
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License