• fullscreen
  • Link.pde
  • Particle.pde
  • fabric.pde
  • // The Link class is used for handling constraints between particles.
    class Link {
      float restingDistance;
      float stiffness;
      
      Particle p1;
      Particle p2;
      
      // the scalars are how much "tug" the particles have on each other
      // this takes into account masses and stiffness, and are set in the Link constructor
      float scalarP1;
      float scalarP2;
      
      // if you want this link to be invisible, set this to false
      boolean drawThis = true;
      
      Link (Particle which1, Particle which2, float restingDist, float stiff) {
        p1 = which1; // when you set one object to another, it's pretty much a reference. 
        p2 = which2; // Anything that'll happen to p1 or p2 in here will happen to the paticles in our array
        
        restingDistance = restingDist;
        stiffness = stiff;
        
        // although there are no differences in masses for the curtain, 
        // this opens up possibilities in the future for if we were to have a fabric with particles of different weights
        float im1 = 1 / p1.mass; // inverse mass quantities
        float im2 = 1 / p2.mass;
        scalarP1 = (im1 / (im1 + im2)) * stiffness;
        scalarP2 = (im2 / (im1 + im2)) * stiffness;
      }
      
      void constraintSolve () {
        // calculate the distance between the two particles
        PVector delta = PVector.sub(p1.position, p2.position);  
        float d = sqrt(delta.x * delta.x + delta.y * delta.y);
        float difference = (restingDistance - d) / d;
        
        // if the distance is more than curtainTearSensitivity, the cloth tears
        // it would probably be better if force was calculated, but this works
        if (d > curtainTearSensitivity) 
          p1.removeLink(this);
        
        // P1.position += delta * scalarP1 * difference
        // P2.position -= delta * scalarP2 * difference
        p1.position.add(PVector.mult(delta, scalarP1 * difference));
        p2.position.sub(PVector.mult(delta, scalarP2 * difference));
      }
    
      void draw () {
        if (drawThis)
          line(p1.position.x, p1.position.y, p2.position.x, p2.position.y);
      }
    }
    
    // the Particle class.
    class Particle {
      PVector lastPosition; // for calculating position change (velocity)
      PVector position;
      
      PVector acceleration; 
      
      float mass = 1;
      float damping = 20;
    
      // An ArrayList for links, so we can have as many links as we want to this particle :)
      ArrayList links = new ArrayList();
      
      boolean pinned = false;
      PVector pinLocation = new PVector(0,0);
      
      // Particle constructor
      Particle (PVector pos) {
        position = pos.get();
        lastPosition = pos.get();
        acceleration = new PVector(0,0);
      }
      
      // The update function is used to update the physics of the particle.
      // motion is applied, and links are drawn here
      void updatePhysics (float timeStep) { // timeStep should be in elapsed seconds (deltaTime)
        // gravity:
        // f(gravity) = m * g
        PVector fg = new PVector(0, mass * gravity);
        this.applyForce(fg);
        
        
        /* Verlet Integration, WAS using http://archive.gamedev.net/reference/programming/features/verlet/ 
           however, we're using the tradition Velocity Verlet integration, because our timestep is now constant. */
        // velocity = position - lastPosition
        PVector velocity = PVector.sub(position, lastPosition);
        // apply damping: acceleration -= velocity * (damping/mass)
        acceleration.sub(PVector.mult(velocity,damping/mass)); 
        // newPosition = position + velocity + 0.5 * acceleration * deltaTime * deltaTime
        PVector nextPos = PVector.add(PVector.add(position, velocity), PVector.mult(PVector.mult(acceleration, 0.5), timeStep * timeStep));
        
        // reset variables
        lastPosition.set(position);
        position.set(nextPos);
        acceleration.set(0,0,0);
      } 
      void updateInteractions () {
        // this is where our interaction comes in.
        if (mousePressed) {
          float distanceSquared = distPointToSegmentSquared(pmouseX,pmouseY,mouseX,mouseY,position.x,position.y);
          if (mouseButton == LEFT) {
            if (distanceSquared < mouseInfluenceSize) { // remember mouseInfluenceSize was squared in setup()
              // To change the velocity of our particle, we subtract that change from the lastPosition.
              // When the physics gets integrated (see updatePhysics()), the change is calculated
              // Here, the velocity is set equal to the cursor's velocity
              lastPosition = PVector.sub(position, new PVector((mouseX-pmouseX)*mouseInfluenceScalar, (mouseY-pmouseY)*mouseInfluenceScalar));
            }
          }
          else { // if the right mouse button is clicking, we tear the cloth by removing links
            if (distanceSquared < mouseTearSize) 
              links.clear();
          }
        }
      }
    
      void draw () {
        // draw the links and points
        stroke(0);
        if (links.size() > 0) {
          for (int i = 0; i < links.size(); i++) {
            Link currentLink = (Link) links.get(i);
            currentLink.draw();
          }
        }
        else
          point(position.x, position.y);
      }
      /* Constraints */
      void solveConstraints () {
        /* Link Constraints */
        // Links make sure particles connected to this one is at a set distance away
        for (int i = 0; i < links.size(); i++) {
          Link currentLink = (Link) links.get(i);
          currentLink.constraintSolve();
        }
        
        /* Boundary Constraints */
        // These if statements keep the particles within the screen
        if (position.y < 1)
          position.y = 2 * (1) - position.y;
        if (position.y > height-1)
          position.y = 2 * (height - 1) - position.y;
        if (position.x > width-1)
          position.x = 2 * (width - 1) - position.x;
        if (position.x < 1)
          position.x = 2 * (1) - position.x;
        
        /* Other Constraints */
        // make sure the particle stays in its place if it's pinned
        if (pinned)
          position.set(pinLocation);
      }
      
      // attachTo can be used to create links between this particle and other particles
      void attachTo (Particle P, float restingDist, float stiff) {
        Link lnk = new Link(this, P, restingDist, stiff);
        links.add(lnk);
      }
      void removeLink (Link lnk) {
        links.remove(lnk);
      }  
     
      void applyForce (PVector f) {
        // acceleration = (1/mass) * force
        // or
        // acceleration = force / mass
        acceleration.add(PVector.div(f, mass));
      }
      
      void pinTo (PVector location) {
        pinned = true;
        pinLocation.set(location);
      }
    } 
    
    /* 
      Curtain (Fabric Simulator)
      Made by BlueThen on February 5th, 2011; updated February 10th and 11th, 2011 and July 18th and 19th, 2011
      To interact, left click and drag, right click to tear, 
                   press 'G' to toggle gravity, and press 'R' to reset
      www.bluethen.com
    */
    
    ArrayList particles;
    
    // every particle within this many pixels will be influenced by the cursor
    float mouseInfluenceSize = 15; 
    // minimum distance for tearing when user is right clicking
    float mouseTearSize = 8;
    float mouseInfluenceScalar = 1;
    
    // force of gravity is really 9.8, but because of scaling, we use 9.8 * 40 (392)
    // (9.8 is too small for a 1 second timestep)
    float gravity = 392; 
    
    // Dimensions for our curtain. These are number of particles for each direction, not actual widths and heights
    // the true width and height can be calculated by multiplying restingDistances by the curtain dimensions
    final int curtainHeight = 56;
    final int curtainWidth = 80;
    final int yStart = 25; // where will the curtain start on the y axis?
    final float restingDistances = 5;
    final float stiffnesses = 1;
    final float curtainTearSensitivity = 50; // distance the particles have to go before ripping
    
    // These variables are used to keep track of how much time is elapsed between each frame
    // they're used in the physics to maintain a certain level of accuracy and consistency
    // this program should run the at the same rate whether it's running at 30 FPS or 300,000 FPS
    long previousTime;
    long currentTime;
    // Delta means change. It's actually a triangular symbol, to label variables in equations
    // some programmers like to call it elapsedTime, or changeInTime. It's all a matter of preference
    // To keep the simulation accurate, we use a fixed time step.
    final int fixedDeltaTime = 15;
    float fixedDeltaTimeSeconds = (float)fixedDeltaTime / 1000.0;
    
    // the leftOverDeltaTime carries over change in time that isn't accounted for over to the next frame
    int leftOverDeltaTime = 0;
    
    // How many times are the constraints solved for per frame:
    int constraintAccuracy = 3;
    
    // instructional stuffs:
    PFont font;
    final int instructionLength = 3000;
    final int instructionFade = 300;
    void setup () {
      // I find that P2D is the fastest renderer for 2D graphics
      // OPENGL may be faster for some people
      // The default renderer is JAVA2D
      size(640,480, P2D);
      
      // we square the mouseInfluenceSize and mouseTearSize so we don't have to use squareRoot when comparing distances with this.
      mouseInfluenceSize *= mouseInfluenceSize; 
      mouseTearSize *= mouseTearSize;
      
      // create the curtain
      createCurtain();
      
      font = loadFont("LucidaBright-Demi-16.vlw");
      textFont(font);
    }
    
    void draw () {
      background(255);
      
      /******** Physics ********/
      // time related stuff
      currentTime = millis();
      // deltaTimeMS: change in time in milliseconds since last frame
      long deltaTimeMS = currentTime - previousTime;
      previousTime = currentTime; // reset previousTime
      // timeStepAmt will be how many of our fixedDeltaTime's can fit in the physics for this frame. 
      int timeStepAmt = (int)((float)(deltaTimeMS + leftOverDeltaTime) / (float)fixedDeltaTime);
      // Here we cap the timeStepAmt to prevent the iteration count from getting too high and exploding
      timeStepAmt = min(timeStepAmt, 5);
      
      leftOverDeltaTime += (int)deltaTimeMS - (timeStepAmt * fixedDeltaTime); // add to the leftOverDeltaTime.
    
      // If the mouse is pressing, it's influence will be spread out over every iteration in equal parts.
      // This keeps the program from exploding from user interaction if the timeStepAmt gets too high.
      mouseInfluenceScalar = 1.0 / timeStepAmt;
      
      // update physics
      for (int iteration = 1; iteration <= timeStepAmt; iteration++) {
        // solve the constraints multiple times
        // the more it's solved, the more accurate.
        for (int x = 0; x < constraintAccuracy; x++) {
          for (int i = 0; i < particles.size(); i++) {
            Particle particle = (Particle) particles.get(i);
            particle.solveConstraints();
          }
        }
        
        // update each particle's position
        for (int i = 0; i < particles.size(); i++) {
          Particle particle = (Particle) particles.get(i);
          particle.updateInteractions();
          particle.updatePhysics(fixedDeltaTimeSeconds);
        }
      }
      // draw each particle or its links
      for (int i = 0; i < particles.size(); i++) {
        Particle particle = (Particle) particles.get(i);
        particle.draw();
      }
      
      if (millis() < instructionLength)
        drawInstructions();
     
      if (frameCount % 60 == 0)
        println("Frame rate is " + frameRate);
    }
    void createCurtain () {
      // We use an ArrayList instead of an array so we could add or remove particles at will.
      // not that it isn't possible using an array, it's just more convenient this way
      particles = new ArrayList();
      
      // midWidth: amount to translate the curtain along x-axis for it to be centered
      // (curtainWidth * restingDistances) = curtain's pixel width
      int midWidth = (int) (width/2 - (curtainWidth * restingDistances)/2);
      // Since this our fabric is basically a grid of points, we have two loops
      for (int y = 0; y <= curtainHeight; y++) { // due to the way particles are attached, we need the y loop on the outside
        for (int x = 0; x <= curtainWidth; x++) { 
          Particle particle = new Particle(new PVector(midWidth + x * restingDistances, y * restingDistances + yStart));
          
          // attach to 
          // x - 1  and
          // y - 1  
          // particle attachTo parameters: Particle particle, float restingDistance, float stiffness
          // try disabling the next 2 lines (the if statement and attachTo part) to create a hairy effect
          if (x != 0) 
            particle.attachTo((Particle)(particles.get(particles.size()-1)), restingDistances, stiffnesses);
          // the index for the particles are one dimensions, 
          // so we convert x,y coordinates to 1 dimension using the formula y*width+x  
          if (y != 0)
            particle.attachTo((Particle)(particles.get((y - 1) * (curtainWidth+1) + x)), restingDistances, stiffnesses);
            
    /*
          // shearing, presumably. Attaching invisible links diagonally between points can give our cloth stiffness.
          // the stiffer these are, the more our cloth acts like jello. 
          // these are unnecessary for me, so I keep them disabled.
          if ((x != 0) && (y != 0)) 
            particle.attachTo((Particle)(particles.get((y - 1) * (curtainWidth+1) + (x-1))), restingDistances * sqrt(2), 0.1, false);
          if ((x != curtainWidth) && (y != 0))
            particle.attachTo((Particle)(particles.get((y - 1) * (curtainWidth+1) + (x+1))), restingDistances * sqrt(2), 1, true);
    */
          
          // we pin the very top particles to where they are
          if (y == 0)
            particle.pinTo(particle.position);
            
          // add to particle array  
          particles.add(particle);
        }
      }
    }
    
    // Controls. The r key resets the curtain, g toggles gravity
    void keyPressed() {
      if ((key == 'r') || (key == 'R'))
        createCurtain();
      if ((key == 'g') || (key == 'G'))
        toggleGravity();
    }
    void toggleGravity () {
      if (gravity != 0)
        gravity = 0;
      else
        gravity = 392;
    }
    
    void drawInstructions () {
      float fade = 255 - (((float)millis()-(instructionLength - instructionFade)) / instructionFade) * 255;
      stroke(0, fade);
      fill(255, fade);
      rect(0,0, 200,45);
      fill(0, fade);
      text("'r' : reset", 10, 20);
      text("'g' : toggle gravity", 10, 35);
    }
    
    // Credit to: http://www.codeguru.com/forum/showpost.php?p=1913101&postcount=16
    float distPointToSegmentSquared (float lineX1, float lineY1, float lineX2, float lineY2, float pointX, float pointY) {
      float vx = lineX1 - pointX;
      float vy = lineY1 - pointY;
      float ux = lineX2 - lineX1;
      float uy = lineY2 - lineY1;
      
      float len = ux*ux + uy*uy;
      float det = (-vx * ux) + (-vy * uy);
      if ((det < 0) || (det > len)) {
        ux = lineX2 - pointX;
        uy = lineY2 - pointY;
        return min(vx*vx+vy*vy, ux*ux+uy*uy);
      }
      
      det = ux*vy - uy*vx;
      return (det*det) / len;
    }
    

    code

    tweaks (1)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    Jared Counts

    Curtain

    Add to Faves Me Likey@! 393
    You must login/register to add this sketch to your favorites.

    Fabric simulation in Processing. It makes use of
    verlet integration. I did my best to comment the code.

    How to make a Fabric Simulator:
    (Part 1) http://bluethen.com/wordpress/index.php/processing-app/curtain/
    (Part 2) http://bluethen.com/wordpress/index.php/processing-app/updated-cloth-simulator-and-how-to-improve-your-verlet-cloth-simulator/

    Left click and drag to interact.
    Right click to tear.
    Press 'G' to toggle gravity.
    Press 'R' to reset.

    Jacob Haip
    5 Feb 2011
    Very nicely done. The organization and commenting on the code makes this a great sketch.
    Jared Counts
    5 Feb 2011
    Thank you. I try my best to aim for clean and well documented code. I was going to separate the classes into their own files, but forgot to when uploading this.

    I sometimes fear over-commenting it, as editing it later on will be a hassle maneuvering around comments.
    Jared Counts
    5 Feb 2011
    Just added tearing. You can tear the curtain by pushing it too hard (left click), or right clicking.
    Wolfe
    5 Feb 2011
    Amazing!
    Asher Salomon
    6 Feb 2011
    This is one kick @$$ sketch
    bitcraft
    7 Feb 2011
    If you are fast with the mouse you can even tear it apart! This makes me feel like Zorro.
    Awesome!
    Jared Counts
    7 Feb 2011
    bitcraft, try messing with some of the variables such as mouseStrength and mouseInfluenceSize. The curtain will tear to shreds effortlessly if the mouseStrengths set high enough.
    N-I-C-E. My friend Brian, thinks this is wayyy cool!!!
    Jared Counts
    11 Feb 2011
    Hi. I updated the applet. It's now more consistent with different frame rates, and a gravity toggle has been added.

    I'll be updating my blog tomorrow morning with details on what I've done, and how you could make a verlet integrated physics engine more consistent.
    Thang Phan
    3 Mar 2011
    Awesome sketch! well done!
    Wow, this is great!
    Ferhat Sen
    12 Mar 2011
    Hi,

    I have combined your awesome cloth simulator with Kinect.
    http://www.vimeo.com/20964926

    Thank you
    Cool Ferhat Sen. Any plans on sharing the code?
    Jared Counts
    12 Mar 2011
    Ferhat: Awesome!
    Ferhat Sen
    13 Mar 2011
    thank you :)

    I will share the code in my course blog soon. I can comment the link when I post it there.
    Sorry for making you wait. But only for a week
    Pistachio Pony
    17 Mar 2011
    Absolutely beautiful work!!! I'm learning so much from it! Thank you!
    chiaki
    21 Mar 2011
    cooooool.....!!!!
    Cheyanne Rogers
    24 Mar 2011
    man this is fun!
    Kyle McDonald
    23 Apr 2011
    seriously just played with this for the last 10 minutes. the dynamics are perfect.

    and yes, wonderfully documented + organized code. i'd love to see an OF port to get an idea of how fast this could be.
    Jared Counts
    24 Apr 2011
    Hi Kyle. Mind telling me what OF is?

    And thanks for the comment @ Kyle and everybody else. Let me know if there's any errors or bad coding. I appreciate any constructive criticism anyone might add.
    Kyle McDonald
    24 Apr 2011
    OF is short for openFrameworks. it's similar to Processing in that it's aimed at 'creative coding', but OF uses C++ instead of Java, so it can be much faster for these kinds of low-level computations. check it out: http://openframeworks.cc/
    Jared Counts
    24 Apr 2011
    Aw sweet! I'll definitely check it out.
    Ilya Zarembsky
    2 May 2011
    Awesome & beautiful.

    There's a bug; grab the curtain and pull it up until the cursor is above the hanging rod, then move the cursor laterally.
    kim scott
    6 May 2011
    NICE!
    very nice!
    jesus great!
    Daphne Tang
    2 Jun 2011
    This is very nice.. You can even shred it. Genius!
    Simo Endre
    16 Jun 2011
    the most inspiring verlet integration i have ever seen! congrats!
    Marius Gerum
    20 Jun 2011
    AMAZING!!!! AWESOME!!!!
    Ted Brown
    7 Jul 2011
    /bow
    Jared Counts
    19 Jul 2011
    Hey everyone, thanks for the feedback!

    I updated the source to be cleaner, faster, and smoother.

    More info on it here: http://bluethen.com/wordpress/index.php/dev-log/changes-to-curtain/
    Excellent! This is great work. Congratulations.
    Tony Blanco
    4 Aug 2011
    Sweet!
    bejoscha
    11 Aug 2011
    really nice. Thanks for putting it on.
    Paul Myburgh
    9 Sep 2011
    awesome.
    Gerd Platl
    27 Sep 2011
    // Really nice work! I like this simulation!
    // Here's a hint for a small speedup of following function...

    // get squared distance of a point to a line segment
    float distPointToSegmentSquared (float lineX1, float lineY1, float lineX2, float lineY2, float pointX, float pointY) {
    float vx = pointX - lineX1;
    float vy = pointY - lineY1;
    float ux = lineX2 - lineX1;
    float uy = lineY2 - lineY1;

    float det = vx * ux + vy * uy;
    if (det &lt; 0)
    return vx*vx+vy*vy;

    float len = ux*ux + uy*uy;
    if (det &gt; len)
    return sq(lineX2 - pointX) + sq (lineY2 - pointY);

    return sq(ux*vy - uy*vx) / len;
    }
    Ergun Coruh
    22 Oct 2011
    Amazing! Beautiful code too. Thanks for sharing.
    FORMIDABLE monsieur........!
    Hello
    I spent hours to find the font:

    LucidaBright-Demi-16.vlw

    to run the downloaded fabric sketch

    for my Mac Book Pro 10.5

    any ideas?
    thanks in advance

    best
    Peter Hofmann
    21 Mar 2012
    Wow, it's amazing what you can do with Processing. Great work!
    franco e
    23 Mar 2012
    Gottfried, just replace it for another font.

    btw, no more bluethen wordpress?
    Jared Counts
    23 Mar 2012
    franco: Crap! Looks like my domain's expiring. I'll be getting it back up asap.

    Gottfried: You can use any font. The LucidaBright-Demi-16 was made using Processing's Create Font tool, which lets you load whichever font you'd like into the data folder.
    ok, bon super

    merci beaucoup
    oups, I forgot
    suppose You want to change the thickness and/or the wideness of the net's squares sorry my english is limited and the colour where is the best place and how should I do

    I am still a bit new to Processing....

    thanks a lot
    Jared Counts
    24 Mar 2012
    Gottfried: You can change the distances between points by changing the value of restingDistances in fabric.pde


    final float restingDistances = 5; // <- this number

    additionally, you can change the number of links by changing these:

    final int curtainHeight = 56; // <- how many rows there are
    final int curtainWidth = 80; // <- how many columns
    bonjour a tous

    merci once more

    now a (perheps) last question(3), sorry 2 questions
    Q1:
    how to change randomly or "controlled by a variable": control panel
    - the size of the thread
    - distance of the squares

    Q2:
    - how to "introduce" my own paintings into the squares
    - put a set of for exemple 10 paintings via control panel
    - make them working

    globally said : the curtain will become a sort of personally designed tissu

    thanks a lot
    and hughs from Montmartre

    gottfried
    Jared Counts
    25 Mar 2012
    Q1 A: The size of each link is contained within Link.pde. To get to each link, you can access them through the Particle class.

    From fabric.pde, you can access every link by looping through every particle:
    for (int i = 0; i < particles.size(); i++) {
    Particle particle = (Particle) particles.get(i);
    for (int i = 0; i &lt; links.size(); i++) {
    Link currentLink = (Link) links.get(i);
    currentLink.restingDistance = newLength; // newLength being whatever number you want
    }
    }

    If you want to change the stroke width (how thick each line is), you'll need to change Link's draw code. In Link.pde:

    void draw () {
    if (drawThis) {
    strokeWidth(strokeSize); // strokeSize being however thick
    line(p1.position.x, p1.position.y, p2.position.x, p2.position.y);
    }
    }

    Alternatively, you can put strokeWidth(strokeSize) inside Particle's draw method, before it draws the links, or you can put it in fabric.pde, before telling each particle to draw its links.

    If you're wanting to program in some sort of graphical menu, I can't really tell you how to do that in a small post like this (controlP5 is a pretty good GUI libarary, google that). The quickest way to add user control is to add some more if statements in the keyPressed method in fabric.pde:

    if ((key == 'q') || (key == 'Q'))
    // loop through particles and increase stroke size or length,

    Q2 A: I'm guessing you want to texture the whole curtain? That's going to be a bit more challenging. I made a fairly basic textured version of Curtain and uploaded it here: http://bluethen.com/secretApplets/texturedFabric/applet/

    You can access the source files from here: http://bluethen.com/secretApplets/texturedFabric/

    I hope that helps. Good luck.
    many thanks
    I will have to integrate all this

    merci encore
    g
    Awesome! Tearing is great idea and the whole stuff is nice implemented! I'm just learning Processing and this is nice part of material to learn!
    Jared Counts
    9 Apr 2012
    Let me know if you need any help, Pawel!
    I'm in love with this sketch.
    Kate Eisenbraun
    17 Aug 2012
    I like to rip it to shreds and then reset it
    :D *thumbs up*
    Ray Shortridge
    19 Sep 2012
    Brilliant! Having just started data vis I am amazed at what can be achieved. I hope I can be half as good as this.
    Dylan Wynn
    4 Oct 2012
    Hi Jared, Is there a way I could talk to you perhaps e-mail or something? I need your help with something
    Jared Counts
    5 Oct 2012
    Dylan: Sure, email me at bluethen (at) gmail.com
    polguezennec
    9 Nov 2012
    fantastic
    This is the coolest thing I've seen all day, and I've been looking at some pretty cool stuff. I just love playing with it!
    Jared Counts
    16 Dec 2012
    I made some minor changes. It should no longer flicker on slower machines.
    Gene Kao
    21 Feb 2013
    great idea! I like your pinTo haha..... really smart~~
    And I made a little change and try it like 3D, hope you like it :))
    http://www.openprocessing.org/sketch/90549
    Raime Todoca
    3 Mar 2013
    Hey! I tweaked your curtain to make it react to sound input. Had lots of fun with it! Fantastic sketch!

    https://www.youtube.com/watch?v=8gJoALd8qOY
    Jared Counts
    3 Mar 2013
    Raime Todoca, that is awesome!! Play some music over it and see what happens!
    Raime Todoca
    4 Mar 2013
    I find it more funny to mess with it with distorted and huge bass soundwaves :)

    You can get it at https://dl.dropbox.com/u/6708989/processing/fabric.rar if you'd like to
    Ever considered doing this in 3D?
    Great stuff man, im gonna go through your code and try and learn more about this. I'm really interested in the interactive art medium (not video games, per se), and your curtain is a good example of that, but i wanted to add some color to it.

    So, i "tweaked" your code to change color based on velocity. its pretty crude implementation of that, and i really only added like 2 lines of code. None-the-less, i think it looks really cool and i havent even played with the colors all that much, its just red and yellow at the moment.

    Thanks for uploading and commenting the hell out of this thing!
    Jared Counts
    13 Mar 2013
    Anson Mansfield: I have, and I did do a version in 3D! http://bluethen.com/secretapplets/superShadedFabric/applet/

    Tom Sterkenburg: Thanks! If you have any issues, then let me know.
    ale plus+
    6 Apr 2013
    Hi, Jared.
    I recently found this javascript port:
    http://codepen.io/stuffit/details/KrAwx
    ...and thanks for sharing! : )
    This is awesome!
    How do you change the size and color to the curtain? Is it possible to apply an Image to it? Like a JPG texture?

    Been playing around with this for houers XD
    Jared Counts
    9 Apr 2013
    Ale: Yes, I saw it too! It's pretty well-done, despite the "Copyright lonely-pixel" and permissions slapped on there.

    olaf: You certainly can apply an image. I made a version quite awhile back, hosted here: http://bluethen.com/secretapplets/texturedFabric/applet/
    Michael S
    28 Aug 2013
    Wonderful. Truly outstanding.
    claire kang
    29 Oct 2013
    crazy beautiful
    cylia
    20 Nov 2013
    wooooooow !!!!great!!!!!!!
    Thanks for this wonderful implementation and awesome comments, Jared. I made a port of the 2D version to openFrameworks.

    https://github.com/tomana/curtain_OFport
    did you know that your sketch responds differently on a Mac vs. a PC? I initially found your sketch on my PC and your instructions work -left click drag, right click tear. But on a Mac, right and left click both tear. Do you have any idea what might cause that?
    Jared Counts
    7 Feb 2014
    Tom,

    That's wonderful! I actually made a C++ port at around the same time as you.

    Thanks for the contribution.
    Jan Stapler
    12 Feb 2014
    I love this sketch and could play it for ages. Awesome work Jared!

    I thought it would be quite funny to have this on the mobile phone. But I can't get the sketch working in android mode. Does someone have an idea how to make it run for mobile phone?
    Darby Smurf
    3 Apr 2014
    so nice sketch!
    Parag Kulkarni
    5 May 2014
    No wonder this is most awesome sketch i have ever seen.
    leeznp
    5 Jun 2014
    How do i fill window curtain colors? plz help me~
    leeznp
    5 Jun 2014
    How did i fill window curtain colors? plz help me~
    Aloysiusbe
    8 Jul 2014
    wow
    Aloysiusbe
    8 Jul 2014
    wow
    You need to login/register to comment.