The Mathematics of Sex Toys Made Tangible

A while back, I posted a picture of a very early prototype sex toy designed to prevent its wearer from getting off.

This project is entirely separate from, and shouldn’t be confused with, the prototype sex toy that’s operated by the wearer’s brainwaves, which I’m still working on. The sex toy that is designed to keep the wearer from getting off, which I’ve started calling the “Tormentor,” is a vibrator connected to a programmable controller board that’s set up to run at random intervals with random pauses in between–perfect, in other words, for keeping whoever’s wearing it always aware of it, while not letting that person get enough stimulation to reach orgasm.

The very first proof-of-concept version was basically a bare controller board tethered to a laptop and connected to a vibrating egg. As it wasn’t exactly portable, it wasn’t able to do what I wanted, which is to be worn around while shopping or doing housework or otherwise going about one’s business.

So I started working on getting a more portable, self-contained version set up and running.


The Tormentor version 0.2 beta 2 was assembled in a small plastic box I bought from Radio Shack.

I actually got all the components from Radio Shack. I went there wearing bunny ears, which got quite a number of puzzled questions from the staff. The assortment of parts I was buying also got quite a number of puzzled questions from the staff, and the answers I gave led to even more questions, and…yeah. You get the idea.

One of the people who works at the local Radio Shack is a rather fetching young woman named Ashley, which by some complete coincidence is the same name as the person I lost my virginity to in the threesome oh so many years ago. I refrained from flirting with her.

The plastic box needed to be wide enough to accommodate the Arduino board that lives at the heart of the Tormentor. I opted to power the Arduino from a 9-volt battery (which probably wasn’t the greatest choice–more on that in a bit), so it needed to be long enough for both the Arduino and the battery. The only box Radio Shack sells that fit the bill was much deeper than I would have wanted, though that turned out to be a good thing.

The actual interface between the Arduino and the vibrator is quite simple, consisting only of one resistor, one TIP120 Darlington transistor, and one diode. Here’s the schematic:

I built the test version by soldering the components directly to each other and taping them to the vibrator battery pack, but that was a little fragile, so I decided that the 0.2 version would use a small Radio Shack breadboarding circuit board cut down to a small (roughly postage stamp sized) square with a hacksaw.

I opted to use a 3/32″ phone jack to connect the vibrator to the Tormentor, because there are a number of vibrators with detachable battery packs floating around out there, and they all seem to use a 3/32″ jack to connect the vibe to the battery pack.

I started out by mounting a 9V battery clip in the bottom of the box, then mounting the Arduino in the box using velcro straps so it could easily be removed.

I hacked a hole in the side of the case big enough to, I thought, stick a USB cable in so I could reprogram the Arduino without taking it out. That idea worked rather less well than I had expected it to.

Two changes I made to the 0.1 prototype involved adding a power switch and a pair of LEDs, one red and one green. The red LED would be a power indicator; the green LED would light whenever the vibrator was running, so that observers would be able to see when it was on. The vibrator itself would run from two AA batteries, independent of the 9V battery that powered the Arduino.

I soldered the LEDs together to use a common ground with a common dropping resistor; the idea was to drive the power LED from the +5 pin on the Arduino, and to drive the “sexual torment” LED from a digital outpit pin on the Arduino that would be programmed to go high whenever the vibrator ran.

I originally was going to mount the LEDs one beneath the other on the box, but after drilling the holes, realized that it would block access to the USB cable that I thought I might be able to use for on-the-fly reprogramming. So I remounted them side by side on the box.

As it turns out, the USB cable really can’t be used when it’s assembled anyway; I do need to take the Arduino out to reprogram it. So I needn’t have bothered.

You can see here how I intended to mount them, and how I eventually did mount them. The 0.2 prototype has an extra hole where I moved the sexual torment LED.

I wired in the LEDs and power switch and used a hot glue gun to glue the wires along the inside of the case. When I added the 3/32″ phone jack to the top, I did the same thing with it, and also hot-glued in the little circuit board with the interface components.

I also hot-glued the AA battery holders for the batteries that run the vibrator to the top of the case.

By the time it was all done, the box was pretty packed. It’s awfully bulky, and could easily be made smaller with a bit of redesign.

Here’s what the final, completely assembled Tormentor prototype looks like. I mounted a belt clip to the top so that it can be worn on a belt, with the wire for the vibrator sneaking in through the wearer’s waistband.

There are some design issues with this version that I plan to address in the future.

First, 9V batteries are BIG! The project could be shrunk down quite a bit by using a 12-volt “N”-sized battery like the kind used in some remote controls. These batteries are very small and can power the Arduino for a long time.

Second, the Arduino I’m using is pretty bulky itself. The next version will probably use either an Arduino Nano or a Teensy USB microcontroller board (an Arduino-like, but very tiny, prototyping board) as its brain. This will get the size of the main component down to less than a quarter of what it is now.

Third, I wasn’t very careful with the interface components; I could probably get them significantly smaller with a bit of work (and more careful hot glueing).


But how did it work?

Ah, now that’s the REAL question, isn’t it? I wired up my first victim volunteer, lapis-lazuli, when she and her hubby accompanied zaiah and I and an assortment of other friends down to the farm to visit some three-week-old kittens.

She wrote up a rather lovely blog entry about her subjective experience with the Tormentor, so rather than repeating it, I’ll just point you over there.

15 thoughts on “The Mathematics of Sex Toys Made Tangible

  1. You probably want to look at a small Lithium-polymer cell for the Arduino. That’s about the highest density rechargable battery chemistry available at the moment. If you can find an Arduino board that runs off 3.3V you can get away with a single cell LiPoly (about 3.2v-2.4V per cell). Otherwise, you’ll need to use a two-cell pack.

    Lithium-polymer cells do require special charging equipment and you’ll probably want a battery monitor circuit attached to the battery itself. I find the flexibility and increased run time is worth it in the long term.

    It should also be possible to run the Arduino and vibrator off the same battery. A Minty Boost or similar circuit can generate a clean 5V off the two AA batteries. Noise and voltage drop might be a problem, since typical AA batteries don’t have much current source capacity. Again, my inclination if I were building this would be to scrap the original batteries completely, and use a single LiPoly to directly power the vibrator, with the Arduino powered through a boost circuit if 5V is required. I can look up some actual part numbers if you’d like.

  2. You probably want to look at a small Lithium-polymer cell for the Arduino. That’s about the highest density rechargable battery chemistry available at the moment. If you can find an Arduino board that runs off 3.3V you can get away with a single cell LiPoly (about 3.2v-2.4V per cell). Otherwise, you’ll need to use a two-cell pack.

    Lithium-polymer cells do require special charging equipment and you’ll probably want a battery monitor circuit attached to the battery itself. I find the flexibility and increased run time is worth it in the long term.

    It should also be possible to run the Arduino and vibrator off the same battery. A Minty Boost or similar circuit can generate a clean 5V off the two AA batteries. Noise and voltage drop might be a problem, since typical AA batteries don’t have much current source capacity. Again, my inclination if I were building this would be to scrap the original batteries completely, and use a single LiPoly to directly power the vibrator, with the Arduino powered through a boost circuit if 5V is required. I can look up some actual part numbers if you’d like.

  3. /*
    Evil begins here
    */

    int sexPin = 9; // Sex toy on pin 9
    unsigned long randNum;
    int randIntensity;
    int randPattern;
    int maxPattern = 9; // highest pattern #r

    void setup() {
    randomSeed(analogRead(0));
    pinMode(13, OUTPUT);
    pinMode (12, OUTPUT);
    Serial.begin(9600);
    }

    void loop() {

    analogWrite (sexPin, 0); //Turn off toy
    digitalWrite(13, LOW);
    digitalWrite(12, LOW); //Turn off LEDs
    randNum = random (1500,9000)*5; // Get random number
    Serial.print(“Delaying: “);
    Serial.println(randNum); //Debugging
    delay (randNum);

    randPattern=random(maxPattern)+1;
    Serial.print(“Pattern number: “);
    Serial.println(randPattern); //Debugging

    switch(randPattern) {

    case 1:
    // Random intensity for 10-45 seconds
    randNum = random(1000,4500) * 10;
    randIntensity = random (100,255)+1;
    analogWrite (sexPin,randIntensity);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    break;

    case 2:
    // oh eh oh eh oh
    analogWrite (sexPin, 127);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1000);
    analogWrite (sexPin, 255);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1600);
    analogWrite (sexPin, 127);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1000);
    analogWrite (sexPin, 255);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1600);
    analogWrite (sexPin, 127);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1000);
    break;

    case 3:
    // bzz bzz (bzz) bzzzzzzzzzz
    randNum=random(1,3)+1;
    for (int i=0; i <= randNum; i++){ analogWrite (sexPin, 255); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(300); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(150); } randNum=random(2000,12000); analogWrite(sexPin,255); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(randNum); break; case 4: // bzz bzz bzzz bzz bzz bzz analogWrite (sexPin, 127); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(500); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(100); analogWrite (sexPin, 190); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(500); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(100); randNum=random(2000,12000); analogWrite(sexPin,255); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(randNum); randNum=random(4)+3; for (int i=0; i <= randNum; i++){ analogWrite (sexPin, 255); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(300); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(150); } break; case 5: // pulsing randNum=random(8)+3; for (int i=0; i <= randNum; i++){ randIntensity=random(100,255); analogWrite (sexPin, randIntensity); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(300); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(150); } break; case 6: //fade randNum=random(3000,6000); digitalWrite(13, HIGH); digitalWrite(12, HIGH); analogWrite (sexPin, 255); delay(randNum); for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=1) {
    // sets the value (range from 0 to 255):
    analogWrite(sexPin, fadeValue);
    // wait for 30 milliseconds
    delay(40);
    }
    break;

  4. /*
    Evil begins here
    */

    int sexPin = 9; // Sex toy on pin 9
    unsigned long randNum;
    int randIntensity;
    int randPattern;
    int maxPattern = 9; // highest pattern #r

    void setup() {
    randomSeed(analogRead(0));
    pinMode(13, OUTPUT);
    pinMode (12, OUTPUT);
    Serial.begin(9600);
    }

    void loop() {

    analogWrite (sexPin, 0); //Turn off toy
    digitalWrite(13, LOW);
    digitalWrite(12, LOW); //Turn off LEDs
    randNum = random (1500,9000)*5; // Get random number
    Serial.print(“Delaying: “);
    Serial.println(randNum); //Debugging
    delay (randNum);

    randPattern=random(maxPattern)+1;
    Serial.print(“Pattern number: “);
    Serial.println(randPattern); //Debugging

    switch(randPattern) {

    case 1:
    // Random intensity for 10-45 seconds
    randNum = random(1000,4500) * 10;
    randIntensity = random (100,255)+1;
    analogWrite (sexPin,randIntensity);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    break;

    case 2:
    // oh eh oh eh oh
    analogWrite (sexPin, 127);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1000);
    analogWrite (sexPin, 255);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1600);
    analogWrite (sexPin, 127);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1000);
    analogWrite (sexPin, 255);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1600);
    analogWrite (sexPin, 127);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay (1000);
    break;

    case 3:
    // bzz bzz (bzz) bzzzzzzzzzz
    randNum=random(1,3)+1;
    for (int i=0; i <= randNum; i++){ analogWrite (sexPin, 255); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(300); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(150); } randNum=random(2000,12000); analogWrite(sexPin,255); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(randNum); break; case 4: // bzz bzz bzzz bzz bzz bzz analogWrite (sexPin, 127); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(500); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(100); analogWrite (sexPin, 190); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(500); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(100); randNum=random(2000,12000); analogWrite(sexPin,255); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(randNum); randNum=random(4)+3; for (int i=0; i <= randNum; i++){ analogWrite (sexPin, 255); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(300); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(150); } break; case 5: // pulsing randNum=random(8)+3; for (int i=0; i <= randNum; i++){ randIntensity=random(100,255); analogWrite (sexPin, randIntensity); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(300); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(150); } break; case 6: //fade randNum=random(3000,6000); digitalWrite(13, HIGH); digitalWrite(12, HIGH); analogWrite (sexPin, 255); delay(randNum); for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=1) {
    // sets the value (range from 0 to 255):
    analogWrite(sexPin, fadeValue);
    // wait for 30 milliseconds
    delay(40);
    }
    break;

  5. case 7:
    //fadefade
    randNum=random(1000,3000);
    analogWrite (sexPin, 255);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay(randNum);
    for(int fadeValue = 255 ; fadeValue >= 100; fadeValue -=1) {
    analogWrite(sexPin, fadeValue);
    delay(40);
    }
    for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=1) {
    analogWrite(sexPin, fadeValue);
    delay(40);
    }
    break;

    case 8:
    //fade up down
    randNum=random(1000,4000);
    digitalWrite (13,HIGH);
    digitalWrite(12, HIGH);
    for(int fadeValue = 0 ; fadeValue >= 255; fadeValue +=1) {
    analogWrite(sexPin, fadeValue);
    delay(40);
    }
    delay(randNum);
    for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=1) {
    analogWrite(sexPin, fadeValue);
    delay(40);
    }
    break;

    case 9:
    // pulse up
    for (int pulses=1 ; pulses <=10; pulses +=1) { randIntensity=(pulses*20)+55; analogWrite (sexPin, randIntensity); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(300); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(150); } break; } //end switch } //end loop

  6. case 7:
    //fadefade
    randNum=random(1000,3000);
    analogWrite (sexPin, 255);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    delay(randNum);
    for(int fadeValue = 255 ; fadeValue >= 100; fadeValue -=1) {
    analogWrite(sexPin, fadeValue);
    delay(40);
    }
    for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=1) {
    analogWrite(sexPin, fadeValue);
    delay(40);
    }
    break;

    case 8:
    //fade up down
    randNum=random(1000,4000);
    digitalWrite (13,HIGH);
    digitalWrite(12, HIGH);
    for(int fadeValue = 0 ; fadeValue >= 255; fadeValue +=1) {
    analogWrite(sexPin, fadeValue);
    delay(40);
    }
    delay(randNum);
    for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=1) {
    analogWrite(sexPin, fadeValue);
    delay(40);
    }
    break;

    case 9:
    // pulse up
    for (int pulses=1 ; pulses <=10; pulses +=1) { randIntensity=(pulses*20)+55; analogWrite (sexPin, randIntensity); digitalWrite(13, HIGH); digitalWrite(12, HIGH); delay(300); analogWrite (sexPin, 0); digitalWrite(13, LOW); digitalWrite(12, LOW); delay(150); } break; } //end switch } //end loop

  7. Code review:

    case 1:
    // Random intensity for 10-45 seconds
    randNum = random(1000,4500) * 10;
    randIntensity = random (100,255)+1;
    analogWrite (sexPin,randIntensity);
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    break;

    There should be a delay(randNum) before the break. Otherwise this is random intensity for 0 seconds as it immediately goes back to the top of the loop.

    case 8:
    //fade up down
    randNum=random(1000,4000);
    digitalWrite (13,HIGH);
    digitalWrite(12, HIGH);
    for(int fadeValue = 0 ; fadeValue >= 255; fadeValue +=1) {
    analogWrite(sexPin, fadeValue);
    delay(40);
    }

    The for loop should read:
    for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=1) {
    As it is now there's no fade up, just a fade down.

    I'd like to read lapis-lazuli's report but her LiveJournal has been deleted. If you still have a copy please repost it here.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.