locus sonus > audio in art
Lab 2007/2009: Julien Clauss, Alejandro Duque, Scott Fitzgerald, Jérôme Joy, Anne Roquigny, Peter Sinclair.
 contact: info (at)


June 19, 2008

sl tracking

(Scott Fitzgerald)

A birds-eye view of the process required to get the position of an object from Second Life into a Pure Data patch is fairly straightforward. There are some “gotchas” outlined below. These primarily lie in the Second Life domain.

Second Life has its own scripting language, with the uninspired name of Linden Scripting Language (LSL). The scripts can be attached to any in-world object you have appropriate permissions for, including your body.

LSL allows for http requests to be made from inside the world to connect to external content (web pages, audio, video). By making a request to a webserver running PHP, we can send the x,y,z coordinates of the object we are interested in. In this instance we are also sending the object’s rotation along the z-axis in radians.

When the request is made, the PHP parses the information, and opens a UDP socket on the local machine. We have a Pure Data patch listening on the socket for incoming information. The information is routed according to the object’s name, giving us the position of each object inside the virtual space.

There were several different iterations of the LSL script, for various reasons. LSL throttles http requests, limiting them to about 1 second per object. If you have a request attached to a large object (about the size of an average avatar), the object loses “energy” over time, it cannot continue to make requests until that “energy” has replenished (achieved by not making requests).

Another issue we encountered is that the objects will report bogus location data, intermittently reporting their location was somewhere different than where they currently were. This happened irregularly, and usually appeared after about an hour of “rezzing” an object with the script.

The original script we were using only reported an object’s beginning and end position. No updates between were registered. This proved problematic, as objects could move from one side of the space to the other, sounding as if it happened instantaneously.

The second iteration had the objects constantly updating their location, whether they were being moved or not. This became problematic for the reason mentioned above, they would erroneously report their location, stating they were somewhere else (often time this appeared to be a previous location).

A compromise was reached, whereby the object would report its location every second while it was being touched. Random location data would still sneak in while a participant would be moving something. However, it was much less frequent and would not cause problems once the object was positioned. One unfortunate side effect of this is that if an object’s position was changed through collision with another object or avatar, or if momentum carried it away from the place it was last touched, its location would no longer be accurate.

One other bit that needed to be added to the LSL was a boundary checker. We did not want the object to leave the confines of the Locus Sonus parcel of land. However, there was ample opportunity for people to move the objects out of our space. This was alleviated through a small script that constantly checked if the object was within our land’s coordinates. If it was not, it would be turned “phantom” (to allow the object to pass through walls), moved to the silent zone. Once reaching the resting place, it would be returned to its normal “physical” state.

The LSL bit

//get the unique ID of the object
key http_request_id;
//the url/path of the webserver & php
string base_url = "";
vector my_pos = <0,0,0>;
float timetowait = 1.;

	//when the object first appears
	//start a timer that goes off every second

	//when the timer goes off
	//get the objects location
   my_pos = llGetPos();
	//get x/y coordinates
 float x_pos = my_pos.x;
float y_pos = my_pos.y;
//check to see if the object is inside the Locus space
if (y_pos < 159 || y_pos > 208 || x_pos > 99 || x_pos < 60){
    //if it is not, make it pahntom and move it to the silent place
    llSetStatus(STATUS_PHANTOM, TRUE);
} else {
//if it is inside the Locus space
//make it physical, and stop moving it
     llSetStatus(STATUS_PHANTOM, FALSE);
     touch(integer num_detected){
         //if object is being touched
//get rotation
   float rot = llRot2Angle(llGetRot());
//get the objects name
   string name = llKey2Name(llGetKey());
//pack the name, location, and rotation into a stricg 
   string request = "?type=avatar&name=" + name + "&data=" +(string)my_pos + "%20" + (string)rot;

//make the http request
   http_request_id = llHTTPRequest(base_url + request, [], "");
//wait for one second


2) php script


Script for getting object type, name and data via http
and sending to a udp socket
-Scott Fitzgerald apr 08 based on 
- Robb Drinkwater, Aug. '07, Jan. '08

$type = $_REQUEST['type']; // get object type
$obj_name = $_REQUEST['name']; // get object name
$pd_data = $_REQUEST['data']; // get object data

echo "<h2>UDP Connection</h2>\n";

/* Get the IP address for the target host. */
$address = gethostbyname('localhost');

/* Create a UDP/IP socket. */
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($socket === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
} else {
    echo "OK.\n";

//open a port 
echo "Attempting to connect to '$address' on port '13001'...";
$result = socket_connect($socket, $address, 13001);
if ($result === false) {
    echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
} else {
    echo "OK.\n";


/* Catch if data is Second Life vector format and reformat */
if(strstr($pd_data, '<') == true)
    //print "found vector format";
    $as_list = str_replace(array("<",">"), "", $pd_data); // strip lt/gt
    $as_list = str_replace(",", " ", $as_list); // replace commas 
    $pd_data = $as_list;    

$formatted = $type." ".$obj_name." ".$pd_data."\n"; // Formatted as raw 'type','name','data' list

//if the "formated" string length is greater than 1 (i.e. we assume it gets data)
//open socket connection and send data

if (strlen($formatted) > 1)
    socket_send($socket, $formatted, strlen($formatted), MSG_DONTROUTE);


3)pd patch
#N canvas 0 22 450 300 10;
#X obj 67 67 netreceive 13001 1;
#X obj 67 92 route avatar;
#X obj 67 118 route Object1;
#X obj 67 146 unpack 0 0 0 0;
#X floatatom 67 177 5 0 0 0 - - -;
#X floatatom 94 197 5 0 0 0 - - -;
#X floatatom 121 217 5 0 0 0 - - -;
#X floatatom 148 172 5 0 0 0 - - -;
#X text 65 191 x;
#X text 91 215 y;
#X text 120 240 z;
#X text 145 194 rotation (in radians);
#X obj 277 124 expr ($f1*360.)/6.283185;
#X floatatom 277 157 5 0 0 0 - - -;
#X text 313 158 degrees;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 3 0;
#X connect 3 0 4 0;
#X connect 3 1 5 0;
#X connect 3 2 6 0;
#X connect 3 3 7 0;
#X connect 7 0 12 0;
#X connect 12 0 13 0;