-
Notifications
You must be signed in to change notification settings - Fork 103
Spring 2014: Week 6
-
Touch events in p5.js are very similar to mouse events in p5.js.
touchX
andtouchY
are analogous tomouseX
andmouseY
.touchStarted()
~mousePressed()
,touchMoved()
~mouseDragged()
, andtouchEnded()
~mouseReleased()
.function setup() { createCanvas(600, 400); } function touchStarted() { fill(0, 255, 0); ellipse(touchX, touchY, 80, 80); } function touchMoved() { fill(0, 0, 255); ellipse(touchX, touchY, 80, 80); } function touchEnded() { fill(255, 0, 0); ellipse(touchX, touchY, 80, 80); }
-
It can be annoying to switch back between
mouseX/Y
andtouchX/Y
, and mouse events and touch events, or to have two version of your sketch for desktop and mobile. A better solution is to write your code in a way that supports either.// this line defines both `touchStarted` and `mousePressed` as the same function var touchStarted = mousePressed = function() { // using or (`||`) means `x` and `y` will be set to whichever value is non-zero // on desktop they will take the `mouseX/Y` values, on mobile the `touchX/Y` values var x = mouseX || touchX; var y = mouseY || touchY; fill(0, 255, 0); ellipse(x, y, 80, 80); };
-
However, with a mouse there will (usually) only be one x and y position. With touch however, you may be touching the screen with more than one finger. To get the position of all fingers touching the screen, use the
touches[]
array. Each object in the array has two properties,x
andy
. The length of the array will correspond to the number of fingers touching the screen at that time. The order of the touch objects in the array is based on the order that the fingers are added (new touches push on to the end).function touchStarted() { fill(0, 255, 0); for(var i = 0; i < touches.length; i++) { ellipse(touches[i].x, touches[i].y, 80, 80); } }
-
Note that
windowWidth
andwindowHeight
give the dimensions of the inner window. You can use them to automatically size the canvas to fit the window.createCanvas(windowWidth, windowHeight);
-
createCanvas
appends a canvas to the body of your page, using the default styling, this often means there is some margin around the body. To fix this you can add a small bit of CSS to the head of your index.html file.<style> body {margin:0;} </style>
-
http://jsconsole.com is really useful for remote mobile development, it displays the contents of your web console, so you can see
print
orconsole.log
messages, as well as errors.- Go to the site and type
:listen
into the top line. It will give you a script tag with a custom ID to stick into your index.html head.<script src="http://jsconsole.com/remote.js?XXXXXXXX-A2FC-458A-9176-8FD3ED2DF126"></script>
Once you've added this, you can browse to the page on your desktop or mobile device and all the console output will show up on jsconsole.com.
-
If you already have a jsconsole script linked in your HTML and you want to set up jsconsole.com to listen for it, type
:listen XXXXXXXX-A2FC-458A-9176-8FD3ED2DF126
with your tag (the part after the?
). -
This jsconsole tutorial has more info and video demos for iOS and Android.
- Go to the site and type
-
You may notice sometimes on mobile the updated JS file doesn't display, or the screen is white, though it looks fine on your desktop browser. This means that your mobile browser is caching (saving) old versions of the file to speed up performance. You can prevent some of the caching and force JS/CSS refresh by adding
?v=1
to the end of your JS link.<script type="text/javascript" src="sketch.js?v=1"></script>
This is usually enough to keep things working properly, but if it's still getting stuck, try making a small change to the HTML file (add an extra space or line break) and save it, then refresh the page. More info about this issue on stackoverflow.
- http://phonegap.com/ -- build cross-platform mobile applications with JS/CSS/HTML5 and access native device functions, such as camera, accelerometer, contacts, etc
- http://jquerymobile.com -- HTML5-based user interface system designed to make responsive web sites and apps that are accessible on all smartphone, tablet and desktop devices
- http://dojotoolkit.org/features/mobile -- HTML5 mobile JavaScript framework that enables rapid development of mobile web applications with a native look and feel on modern webkit-enabled mobile devices
- http://www.sencha.com/products/touch/ -- HTML5 mobile application JavaScript framework
- http://eightmedia.github.io/hammer.js/ -- JavaScript library for multi-touch gestures, getting started with hammer.js
- https://developer.mozilla.org/en-US/docs/WebAPI
- https://developer.mozilla.org/en-US/docs/Web/Guide/API
Everything we've done this semester has been an example of "client-side" programming. The JavaScript code we write is executed by the browser itself and runs on the user's local computer. For controlling the local behavior of a web page this is enough. However, there are lots of tasks that web applications perform that can only be accomplished with server-side programming. Here are some examples of functionality that would require server-side programming.
- Storing data over time (how many times have users visited this page? What are the high scores of this game? etc.)
- User login / authentication.
- Accessing APIs.
- Connecting to hardware (such as arduino or kinect) -- this is a unique sort of scenario where we may run a server-side program locally on a client machine to transfer data from a hardware device that the browser does not have access to.)
Before we start writing our own servers, we should note that we have been running server-side programs all along. Every time we type python -m SimpleHTTPServer
we are running a simple server program written in python. What does this server program do? It takes requests ("Could I please have that index.html file?") and responds to those requests ("Here, enjoy this index.html file!"). This is all this simple HTTP server can do, handle HTTP Requests and serve up files.
We don't need python for this. We can write server-side programs in all sorts of languages (php, ruby, java, the list goes on) including JavaScript. For example, here's a simple HTTP Server written in JS.
var handleRequest = function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World\n');
};
var http = require('http');
var server = http.createServer(handleRequest);
server.listen(8080);
This server is even more simple than the python one. It doesn't even serve any files. No matter what the request might be, the only response ever is "Hello World". This server is written in JavaScript using node.js, a platform for building JavaScript network applications. We'll be getting to the specifics of how this example works later.
The first thing we need to do to get up and running with server-side programming in JavaScript is to install node. Once you've installed node, to make sure it's working just open up terminal and type node
. If it's installed you'll see a prompt. You can type any JavaScript here just like in the browser console to test (hit cntrl-c twice to exit.)
Now make any old file with JavaScript code in it. For example, make a file called "hello.js" with:
console.log('Hello!');
Now back in terminal type node hello.js
. You should see Hello!
back in the console.
Congratulations, you are now executing server-side JS code via the command line!
The next step we need to do is to write a server program that handles HTTP requests. An HTTP request is that thing that happens when you type something into the address bar of your web browser. http://wwww.google.com
makes a request to google's server which then sends you a response (filled with the HTML found in google's homepage.)
To write a Node server that handles requests, we write a function that receives a request argument and acts on a response argument.
var handleRequest = function (request, response) {
// Handle the request with a response
};
Now we pass that function as a callback to a createServer()
method and listen on a port number.
var server = http.createServer(handleRequest);
server.listen(8080);
We are missing one line of code, however. Where does http
come from? We need to import the Node "http" module. var http = require('http');
Node comes with a few built-in modules, and there are 70,000 and counting more you can install via npm.
Let's put all the code from above together and add one more line. We'll send back the text "Hello World!" for every HTTP request made to the server.
// This function handles an incoming "request"
// And sends back out a "response";
var handleRequest = function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World\n');
};
// HTTP module
var http = require('http');
// Create a server with the handleRequest callback
var server = http.createServer(handleRequest);
// Listen on port 8080
server.listen(8080);
console.log('Server started on port 8080');
If we save this as server.js
we can then execute the server via node server.js
. Navigate to your browser and type localhost:8080
into the address bar and you should see "Hello world".
We certainly aren't the first to build a server application with Node. And because just about all servers need a lot of the same code, many developers will use an abstraction on top of Node such as express, a web application framework for node. The getting started Express guide will give you a good basic sense of how such a framework works.
For us, we're going to bypass express and do a couple more basic things with Node as it relates to p5. For example, how would we serve a p5 sketch? The first thing we need to learn is how to use the file system module to read in a file (say index.html
) and write the contents out as a response to a request.
// The file system module
var fs = require('fs');
function handleRequest(request, response) {
// User file system module to read index.html file
fs.readFile(__dirname + '/index.html',
// Callback function for reading
function (err, data) {
// if there is an error
if (err) {
response.writeHead(500);
return response.end('Error loading index.html');
}
// Otherwise, send the data, the contents of the file
response.writeHead(200);
response.end(data);
}
);
}
We're really living in JavaScript land now. The readFile()
function takes two arguments. The first argument is the path of the file we want to read and the second is a function that will be triggered once the file has been read. Note how we are passing in an anonymous function as an argument to the readFile()
function!
While this server works, it will still only send back index.html
no matter what the user actually requests. What if you are serving multiple pages including html, JS, and CSS files? In this case, we'll want to read in the actual request from the user via request.url
. We can then serve the file back up with the appropriate type (looking at its file extension).
var http = require('http');
var path = require('path');
var ext = require('ext');
function handleRequest(req, res) {
// What did we request?
var pathname = req.url;
// If blank let's ask for index.html
if (pathname == '/') {
pathname = '/index.html';
}
// Ok what's our file extension
var ext = path.extname(pathname);
// Map extension to file type
var typeExt = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css'
};
// What is it? Default to plain text
var contentType = typeExt[ext] || 'text/plain';
// Now read and write back the file with the appropriate content type
fs.readFile(__dirname + pathname,
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading ' + pathname);
}
// Dynamically setting content type
res.writeHead(200,{ 'Content-Type': contentType });
res.end(data);
}
);
}
This handleRequest()
method will serve a p5 sketch including an index.html
, sketch.js
, and style.css
. You can see the full example here.
If we want a client to have a synchronous connection with other clients (for example, in a chat application) we can use web sockets. With p5, for example, we could send the mouse location from one sketch to another to create a multi-user whiteboard application. To accomplish this, we'll need to install the socket.io module.
$ npm install socket.io
Now in our server program, we can listen for socket connections.
var server = http.createServer(handleRequest);
server.listen(8080);
var io = require('socket.io').listen(server);
The io
object can be passed callback functions to handle events. For example, when a new connection is made, we could pass in a function that prints out a notice to the console.
// Register a callback function to run when we have an individual connection
// This is run for each individual user that connects
io.sockets.on('connection',
function (socket) {
console.log("We have a new client: " + socket.id);
}
);
The anonymous callback function receives a socket argument. This socket argument is a reference for the current client connected. That client can also be passed callback functions. For example, here is a callback for when a client disconnects.
// We are given a websocket object in our function
function (socket) {
console.log("We have a new client: " + socket.id);
socket.on('disconnect', function() {
console.log("Client has disconnected");
});
}
We can also create custom callbacks for certain kinds of events. Let's make up an event called "mouse." Our p5 sketch will send mouseX and mouseY for each "mouse" event. The server can handle that event by sending back that data to any other connected clients.
socket.on('mouse',
function(data) {
// Data comes in as whatever was sent, including objects
console.log("Received: 'mouse' " + data.x + " " + data.y);
// Send it to all other clients
socket.broadcast.emit('mouse', data);
}
);
Now we have a socket server that receives mouse coordinates and passes them back to all other connected clients. (Note that the server does not broadcast the data back to the sender itself; that can be accomplished with io.sockets.emit('mouse', data);
.)
All we have left to do is broadcast and receive the mouse values from our p5 sketch itself. Remember this is the part that is executed by the client browser itself and is not part of the server program. Here, we use the same socket.io module in setup()
.
var socket;
function setup() {
socket = io.connect('http://localhost');
}
Note how our socket is connecting to "localhost". This is because we are just running everything locally on our machine for development. Eventually you'll want to deploy your app to an actual server, and you'll need the URL or IP address of your server in that case. (More on this later).
Once we have our socket, we can broadcast mouseX and mouseY. Let's do it whenever the user drags the mouse.
function mouseDragged() {
// Make a little object with mouseX and mouseY
var data = {
x: mouseX,
y: mouseY
};
// Send that object to the socket
socket.emit('mouse',data);
}
Oh, and we forgot one more thing. We need to do something with the data when we receive it! Here we'll create an anonymous function that will handle our "mouse" event.
function setup() {
socket = io.connect('http://localhost');
// We make a named event called 'mouse' and write an
// anonymous callback function
socket.on('mouse',
function(data) {
// Draw a blue circle
fill(0,0,255);
noStroke();
ellipse(data.x,data.y,80,80);
}
);
}
See the full example for all the pieces working together.
Tom Igoe also has a great tutorial for using a web socket to send data from an arduino.
This wiki only covers how to set up and run simple node servers locally. When you want to release your app into the wild, you'll need an actual server to run your app on.
- Node.js running on heroku
- Node.js and Amazon web services
- From Shawn's Live Web course: Sam Slover's notes on Node.js and Amazon EC2