Sync JS time between multiple devices
I'm using the wonderful reveal.js library to create a HTML slideshow. My only problem is that I need it to synchronise across multiple devices.
At the moment I am making a AJAX request to the the time from the server and keep an internal clock for the page.
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
r.open('HEAD', document.location, false);
r.send(null);
var timestring = r.getResponseHeader("DATE");
systemtime = new Date(timestring); // Set the time to the date sent from the server
}
Whilst this gets me within 1 or so seconds of accuracy, I need to do better. The difference is really noticeable when the slideshow is auto advancing.
The code is going to be running all on the same platform, so cross-browser compatibility is nothing to worry about.
Here's what I've managed to put together
Any ideas?
javascript time synchronization
add a comment |
I'm using the wonderful reveal.js library to create a HTML slideshow. My only problem is that I need it to synchronise across multiple devices.
At the moment I am making a AJAX request to the the time from the server and keep an internal clock for the page.
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
r.open('HEAD', document.location, false);
r.send(null);
var timestring = r.getResponseHeader("DATE");
systemtime = new Date(timestring); // Set the time to the date sent from the server
}
Whilst this gets me within 1 or so seconds of accuracy, I need to do better. The difference is really noticeable when the slideshow is auto advancing.
The code is going to be running all on the same platform, so cross-browser compatibility is nothing to worry about.
Here's what I've managed to put together
Any ideas?
javascript time synchronization
add a comment |
I'm using the wonderful reveal.js library to create a HTML slideshow. My only problem is that I need it to synchronise across multiple devices.
At the moment I am making a AJAX request to the the time from the server and keep an internal clock for the page.
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
r.open('HEAD', document.location, false);
r.send(null);
var timestring = r.getResponseHeader("DATE");
systemtime = new Date(timestring); // Set the time to the date sent from the server
}
Whilst this gets me within 1 or so seconds of accuracy, I need to do better. The difference is really noticeable when the slideshow is auto advancing.
The code is going to be running all on the same platform, so cross-browser compatibility is nothing to worry about.
Here's what I've managed to put together
Any ideas?
javascript time synchronization
I'm using the wonderful reveal.js library to create a HTML slideshow. My only problem is that I need it to synchronise across multiple devices.
At the moment I am making a AJAX request to the the time from the server and keep an internal clock for the page.
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
r.open('HEAD', document.location, false);
r.send(null);
var timestring = r.getResponseHeader("DATE");
systemtime = new Date(timestring); // Set the time to the date sent from the server
}
Whilst this gets me within 1 or so seconds of accuracy, I need to do better. The difference is really noticeable when the slideshow is auto advancing.
The code is going to be running all on the same platform, so cross-browser compatibility is nothing to worry about.
Here's what I've managed to put together
Any ideas?
javascript time synchronization
javascript time synchronization
edited May 20 '12 at 12:40
asked May 14 '12 at 15:02
Josh Hunt
4,089236792
4,089236792
add a comment |
add a comment |
5 Answers
5
active
oldest
votes
How about a different approach: who cares about time? (You're not going to reliably sync the system clock with JavaScript.)
Instead, use a Node server with socket.io to synchronize when your clients advance the slideshow. Instead of the clients deciding when to advance, the server tells them to.
This approach comes with the added bonus of being able to manually fiddle with the slideshow while it's running. In the example that follows, I've added a Next button that causes all connected clients to immediately advance to the next slide.
app.js
var express = require('express')
, app = express.createServer()
, io = require('socket.io').listen(app)
, doT = require('dot')
, slide = 0
, slides = [
'http://placekitten.com/700/400?image=13',
'http://placekitten.com/700/400?image=14',
'http://placekitten.com/700/400?image=15',
'http://placekitten.com/700/400?image=16',
'http://placekitten.com/700/400?image=1',
'http://placekitten.com/700/400?image=2',
'http://placekitten.com/700/400?image=3',
'http://placekitten.com/700/400?image=4',
'http://placekitten.com/700/400?image=5',
'http://placekitten.com/700/400?image=6',
'http://placekitten.com/700/400?image=7',
'http://placekitten.com/700/400?image=8',
'http://placekitten.com/700/400?image=9',
'http://placekitten.com/700/400?image=10',
'http://placekitten.com/700/400?image=11',
'http://placekitten.com/700/400?image=12',
];
app.listen(70); // listen on port 70
app.register('.html', doT); // use doT to render templates
app.set('view options', {layout:false}); // keep it simple
doT.templateSettings.strip=false; // don't strip line endings from template file
app.get('/', function(req, res) {
res.render('index.html', { slide: slide, slides: slides });
});
app.post('/next', function(req, res) {
next();
res.send(204); // No Content
});
setInterval(next, 4000); // advance slides every 4 seconds
function next() {
if (++slide >= slides.length) slide = 0;
io.sockets.emit('slide', slide);
}
views/index.html
This file is processed as a doT template.
<!DOCTYPE html>
<html>
<head>
<title>Synchronized Slideshow</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var curslide = {{=it.slide}}; // the slide the server is currently on.
$(function() {
$('#slide' + curslide).css('left',0);
$('#next').click(function() {
$.post('/next');
});
});
var socket = io.connect('http://localhost:70');
socket.on('slide', function(slide) {
$('#slide' + curslide).animate({left:-700}, 400);
$('#slide' + slide).css('left',700).show().animate({left:0}, 400);
curslide = slide;
});
</script>
<style>
#slideshow, .slide { width:700px; height:400px; overflow:hidden; position:relative; }
.slide { position:absolute; top:0px; left:700px; }
</style>
</head>
<body>
<div id="slideshow">
{{~it.slides :url:i}}
<div id="slide{{=i}}" class="slide"><img src="{{=url}}"></div>
{{~}}
</div>
<button id="next">Next ></button>
</body>
</html>
Copy these two files into a folder, then run
$ npm install express socket.io dot
$ node app.js
and navigate to http://localhost:70
in several different windows, then see the magic.
Love this answer! Ignoring my question to get to the same objective. I'm going to take this approach and see what I can get out of it!
– Josh Hunt
May 20 '12 at 2:27
And kudos for the demo link!
– Josh Hunt
May 20 '12 at 2:33
I got a couple of errors running this with the latest version of Express. Only when I pinned it to version 2.5.10 and reinstalled did it work. Also, I had to runsudo node app.js
– skube
Dec 30 '16 at 15:27
1
Yeah, this is very old and express obviously has many breaking changes between v2 and v4. The need forsudo
comes from listening on port 70, change that to something >1024 to fix.
– josh3736
Jan 2 '17 at 2:28
add a comment |
Measure the elapsed time between sending the request and getting back the response. Then, divide that value by 2. That gives you a rough value of one-way latency. If you add that to the time value from the server, you'll be closer to the true server time.
Something like this should work:
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
var start = (new Date).getTime();
r.open('HEAD', document.location, false);
r.onreadystatechange = function()
{
if (r.readyState != 4)
{
return;
}
var latency = (new Date).getTime() - start;
var timestring = r.getResponseHeader("DATE");
// Set the time to the **slightly old** date sent from the
// server, then adjust it to a good estimate of what the
// server time is **right now**.
systemtime = new Date(timestring);
systemtime.setMilliseconds(systemtime.getMilliseconds() + (latency / 2))
};
r.send(null);
}
Interesting aside: John Resig has a good article on the accuracy of Javascript timing.
It shouldn't cause a problem in this case, since you're only concerned about your time being off by ~1 second. A 15 ms difference shouldn't have much effect.
An amazing answer! Pretty sure this is the best solution available, but im going to add a bounty on to see if I can attract some other solutions.
– Josh Hunt
May 16 '12 at 8:14
4
This is how I would do it. The only difference being that I would do a sync call several times (i.e. 10 times) and then use the systemtime from the call with the lowest latency. Indeed, the higher the latency, the more impact you have from the fact that the distribution between from-client-to-server-time and from-server-to-client-time is not exactly 1:1.
– Willem Mulder
May 19 '12 at 12:50
Is there a better option than dividing response time by 2? In some of my tests the server blocks for awhile but then returns quickly. So maybe the trip time was 200ms, but the time returned is only 20ms off. Does that make sense?
– jocull
Jul 23 '13 at 15:27
add a comment |
I'm glad you found a satisfactory answer to your question. I had a similar need to synchronize the browser with the server's clock and was determined to achieve it with better than 1 second accuracy like you were. I've written code to do this and am posting this answer here in case anyone else needs the solution too.
The code is called ServerDate and is freely available for download. Here's part of the README. Notice that I achieved 108 ms precision in my example:
You can use ServerDate
as you would use the Date
function or one of its
instances, e.g.:
> ServerDate()
"Mon Aug 13 2012 20:26:34 GMT-0300 (ART)"
> ServerDate.now()
1344900478753
> ServerDate.getMilliseconds()
22
There is also a new method to get the precision of ServerDate's estimate of the
server's clock (in milliseconds):
> ServerDate.toLocaleString() + " ± " + ServerDate.getPrecision() + " ms"
"Tue Aug 14 01:01:49 2012 ± 108 ms"
You can see the difference between the server's clock and the browsers clock, in milliseconds:
> ServerDate - new Date()
39
add a comment |
You can't really sync up with the server. Measuring the time your server request needs (as MikeWyatt suggested) is not a good indicator on the latency.
Only your server knows when he responds a request. Therefore, it should send that information back with the answer. With Date.now() - new Date(timestringOfServerResponse)
you can measure the latency exactly. Yet I'm not sure why you would need that value.
To sync an app between mulitiple devices, the server should send them which action to perform when. The "when" should not be "as soon as you get my response", but a exact timestamp. As far as the system clocks of your devices are accurate and synced (they usually are), the app will run its methods synchrounously, because it knows what to happen when (or at least: what should have happened then, and it can interpolate what to happen "now").
1
That's exactly how this is functioning. The page waits until the correct time before performing an action to ensure it is syncd across devices. The problem comes down to the fact that I have two iPads next to each other which are set to sync time with time servers, yet are 1 second apart.
– Josh Hunt
May 15 '12 at 11:20
So you want to implement a time server in javascript, to sync "custom system times"?
– Bergi
May 15 '12 at 11:25
2
@Bergi, you're erroneously assuming that the client's clock matches the clock on the server. They are practically guaranteed to be different, probably by multiple seconds or minutes. Adjusting for latency isn't perfect, but it will get you pretty close. In the worst case, you'll be off by the full round-trip time, if one of the trips was instantaneous.
– MikeWyatt
May 16 '12 at 3:47
add a comment |
I'm extensively using the COMET pattern here for my real time web application.
To use that in your case you'd need the clients to open an AJAX request to the server and wait for an answer. As soon as it comes the client has to change slides.
On the server you have to hold back all answers till it's time to change slides. (You could be more advanced and delay afterwards on the client for the same time each, but that's most likely not necessary). I can't show you sample code for that here as I don't know what's available to you.
So you are effectively creating an orchestra where the server plays the conductor and all clients are listening to him.
Timing is then determined by the ability of the server to answer the requests at (nearly) the same time plus the network latency.
Usually the clients should be in the same part of the network so latency might be very similar - and the absolute value doesn't hurt here, only the variation.
And there might also be an additional trick helping: don't change the slides with a hard replacement, blend them. This will blur the change so that the eye can't catch the little timing differences that you'll always have.
(If you can't have the server playing conductor you're likely to have to use the solution by MikeWyatt - probably with a few requests and averaging the result, depending on the network setup. In a LAN one request might be enough, going over the full internet a bit over averaging won't hurt...)
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f10585910%2fsync-js-time-between-multiple-devices%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
How about a different approach: who cares about time? (You're not going to reliably sync the system clock with JavaScript.)
Instead, use a Node server with socket.io to synchronize when your clients advance the slideshow. Instead of the clients deciding when to advance, the server tells them to.
This approach comes with the added bonus of being able to manually fiddle with the slideshow while it's running. In the example that follows, I've added a Next button that causes all connected clients to immediately advance to the next slide.
app.js
var express = require('express')
, app = express.createServer()
, io = require('socket.io').listen(app)
, doT = require('dot')
, slide = 0
, slides = [
'http://placekitten.com/700/400?image=13',
'http://placekitten.com/700/400?image=14',
'http://placekitten.com/700/400?image=15',
'http://placekitten.com/700/400?image=16',
'http://placekitten.com/700/400?image=1',
'http://placekitten.com/700/400?image=2',
'http://placekitten.com/700/400?image=3',
'http://placekitten.com/700/400?image=4',
'http://placekitten.com/700/400?image=5',
'http://placekitten.com/700/400?image=6',
'http://placekitten.com/700/400?image=7',
'http://placekitten.com/700/400?image=8',
'http://placekitten.com/700/400?image=9',
'http://placekitten.com/700/400?image=10',
'http://placekitten.com/700/400?image=11',
'http://placekitten.com/700/400?image=12',
];
app.listen(70); // listen on port 70
app.register('.html', doT); // use doT to render templates
app.set('view options', {layout:false}); // keep it simple
doT.templateSettings.strip=false; // don't strip line endings from template file
app.get('/', function(req, res) {
res.render('index.html', { slide: slide, slides: slides });
});
app.post('/next', function(req, res) {
next();
res.send(204); // No Content
});
setInterval(next, 4000); // advance slides every 4 seconds
function next() {
if (++slide >= slides.length) slide = 0;
io.sockets.emit('slide', slide);
}
views/index.html
This file is processed as a doT template.
<!DOCTYPE html>
<html>
<head>
<title>Synchronized Slideshow</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var curslide = {{=it.slide}}; // the slide the server is currently on.
$(function() {
$('#slide' + curslide).css('left',0);
$('#next').click(function() {
$.post('/next');
});
});
var socket = io.connect('http://localhost:70');
socket.on('slide', function(slide) {
$('#slide' + curslide).animate({left:-700}, 400);
$('#slide' + slide).css('left',700).show().animate({left:0}, 400);
curslide = slide;
});
</script>
<style>
#slideshow, .slide { width:700px; height:400px; overflow:hidden; position:relative; }
.slide { position:absolute; top:0px; left:700px; }
</style>
</head>
<body>
<div id="slideshow">
{{~it.slides :url:i}}
<div id="slide{{=i}}" class="slide"><img src="{{=url}}"></div>
{{~}}
</div>
<button id="next">Next ></button>
</body>
</html>
Copy these two files into a folder, then run
$ npm install express socket.io dot
$ node app.js
and navigate to http://localhost:70
in several different windows, then see the magic.
Love this answer! Ignoring my question to get to the same objective. I'm going to take this approach and see what I can get out of it!
– Josh Hunt
May 20 '12 at 2:27
And kudos for the demo link!
– Josh Hunt
May 20 '12 at 2:33
I got a couple of errors running this with the latest version of Express. Only when I pinned it to version 2.5.10 and reinstalled did it work. Also, I had to runsudo node app.js
– skube
Dec 30 '16 at 15:27
1
Yeah, this is very old and express obviously has many breaking changes between v2 and v4. The need forsudo
comes from listening on port 70, change that to something >1024 to fix.
– josh3736
Jan 2 '17 at 2:28
add a comment |
How about a different approach: who cares about time? (You're not going to reliably sync the system clock with JavaScript.)
Instead, use a Node server with socket.io to synchronize when your clients advance the slideshow. Instead of the clients deciding when to advance, the server tells them to.
This approach comes with the added bonus of being able to manually fiddle with the slideshow while it's running. In the example that follows, I've added a Next button that causes all connected clients to immediately advance to the next slide.
app.js
var express = require('express')
, app = express.createServer()
, io = require('socket.io').listen(app)
, doT = require('dot')
, slide = 0
, slides = [
'http://placekitten.com/700/400?image=13',
'http://placekitten.com/700/400?image=14',
'http://placekitten.com/700/400?image=15',
'http://placekitten.com/700/400?image=16',
'http://placekitten.com/700/400?image=1',
'http://placekitten.com/700/400?image=2',
'http://placekitten.com/700/400?image=3',
'http://placekitten.com/700/400?image=4',
'http://placekitten.com/700/400?image=5',
'http://placekitten.com/700/400?image=6',
'http://placekitten.com/700/400?image=7',
'http://placekitten.com/700/400?image=8',
'http://placekitten.com/700/400?image=9',
'http://placekitten.com/700/400?image=10',
'http://placekitten.com/700/400?image=11',
'http://placekitten.com/700/400?image=12',
];
app.listen(70); // listen on port 70
app.register('.html', doT); // use doT to render templates
app.set('view options', {layout:false}); // keep it simple
doT.templateSettings.strip=false; // don't strip line endings from template file
app.get('/', function(req, res) {
res.render('index.html', { slide: slide, slides: slides });
});
app.post('/next', function(req, res) {
next();
res.send(204); // No Content
});
setInterval(next, 4000); // advance slides every 4 seconds
function next() {
if (++slide >= slides.length) slide = 0;
io.sockets.emit('slide', slide);
}
views/index.html
This file is processed as a doT template.
<!DOCTYPE html>
<html>
<head>
<title>Synchronized Slideshow</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var curslide = {{=it.slide}}; // the slide the server is currently on.
$(function() {
$('#slide' + curslide).css('left',0);
$('#next').click(function() {
$.post('/next');
});
});
var socket = io.connect('http://localhost:70');
socket.on('slide', function(slide) {
$('#slide' + curslide).animate({left:-700}, 400);
$('#slide' + slide).css('left',700).show().animate({left:0}, 400);
curslide = slide;
});
</script>
<style>
#slideshow, .slide { width:700px; height:400px; overflow:hidden; position:relative; }
.slide { position:absolute; top:0px; left:700px; }
</style>
</head>
<body>
<div id="slideshow">
{{~it.slides :url:i}}
<div id="slide{{=i}}" class="slide"><img src="{{=url}}"></div>
{{~}}
</div>
<button id="next">Next ></button>
</body>
</html>
Copy these two files into a folder, then run
$ npm install express socket.io dot
$ node app.js
and navigate to http://localhost:70
in several different windows, then see the magic.
Love this answer! Ignoring my question to get to the same objective. I'm going to take this approach and see what I can get out of it!
– Josh Hunt
May 20 '12 at 2:27
And kudos for the demo link!
– Josh Hunt
May 20 '12 at 2:33
I got a couple of errors running this with the latest version of Express. Only when I pinned it to version 2.5.10 and reinstalled did it work. Also, I had to runsudo node app.js
– skube
Dec 30 '16 at 15:27
1
Yeah, this is very old and express obviously has many breaking changes between v2 and v4. The need forsudo
comes from listening on port 70, change that to something >1024 to fix.
– josh3736
Jan 2 '17 at 2:28
add a comment |
How about a different approach: who cares about time? (You're not going to reliably sync the system clock with JavaScript.)
Instead, use a Node server with socket.io to synchronize when your clients advance the slideshow. Instead of the clients deciding when to advance, the server tells them to.
This approach comes with the added bonus of being able to manually fiddle with the slideshow while it's running. In the example that follows, I've added a Next button that causes all connected clients to immediately advance to the next slide.
app.js
var express = require('express')
, app = express.createServer()
, io = require('socket.io').listen(app)
, doT = require('dot')
, slide = 0
, slides = [
'http://placekitten.com/700/400?image=13',
'http://placekitten.com/700/400?image=14',
'http://placekitten.com/700/400?image=15',
'http://placekitten.com/700/400?image=16',
'http://placekitten.com/700/400?image=1',
'http://placekitten.com/700/400?image=2',
'http://placekitten.com/700/400?image=3',
'http://placekitten.com/700/400?image=4',
'http://placekitten.com/700/400?image=5',
'http://placekitten.com/700/400?image=6',
'http://placekitten.com/700/400?image=7',
'http://placekitten.com/700/400?image=8',
'http://placekitten.com/700/400?image=9',
'http://placekitten.com/700/400?image=10',
'http://placekitten.com/700/400?image=11',
'http://placekitten.com/700/400?image=12',
];
app.listen(70); // listen on port 70
app.register('.html', doT); // use doT to render templates
app.set('view options', {layout:false}); // keep it simple
doT.templateSettings.strip=false; // don't strip line endings from template file
app.get('/', function(req, res) {
res.render('index.html', { slide: slide, slides: slides });
});
app.post('/next', function(req, res) {
next();
res.send(204); // No Content
});
setInterval(next, 4000); // advance slides every 4 seconds
function next() {
if (++slide >= slides.length) slide = 0;
io.sockets.emit('slide', slide);
}
views/index.html
This file is processed as a doT template.
<!DOCTYPE html>
<html>
<head>
<title>Synchronized Slideshow</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var curslide = {{=it.slide}}; // the slide the server is currently on.
$(function() {
$('#slide' + curslide).css('left',0);
$('#next').click(function() {
$.post('/next');
});
});
var socket = io.connect('http://localhost:70');
socket.on('slide', function(slide) {
$('#slide' + curslide).animate({left:-700}, 400);
$('#slide' + slide).css('left',700).show().animate({left:0}, 400);
curslide = slide;
});
</script>
<style>
#slideshow, .slide { width:700px; height:400px; overflow:hidden; position:relative; }
.slide { position:absolute; top:0px; left:700px; }
</style>
</head>
<body>
<div id="slideshow">
{{~it.slides :url:i}}
<div id="slide{{=i}}" class="slide"><img src="{{=url}}"></div>
{{~}}
</div>
<button id="next">Next ></button>
</body>
</html>
Copy these two files into a folder, then run
$ npm install express socket.io dot
$ node app.js
and navigate to http://localhost:70
in several different windows, then see the magic.
How about a different approach: who cares about time? (You're not going to reliably sync the system clock with JavaScript.)
Instead, use a Node server with socket.io to synchronize when your clients advance the slideshow. Instead of the clients deciding when to advance, the server tells them to.
This approach comes with the added bonus of being able to manually fiddle with the slideshow while it's running. In the example that follows, I've added a Next button that causes all connected clients to immediately advance to the next slide.
app.js
var express = require('express')
, app = express.createServer()
, io = require('socket.io').listen(app)
, doT = require('dot')
, slide = 0
, slides = [
'http://placekitten.com/700/400?image=13',
'http://placekitten.com/700/400?image=14',
'http://placekitten.com/700/400?image=15',
'http://placekitten.com/700/400?image=16',
'http://placekitten.com/700/400?image=1',
'http://placekitten.com/700/400?image=2',
'http://placekitten.com/700/400?image=3',
'http://placekitten.com/700/400?image=4',
'http://placekitten.com/700/400?image=5',
'http://placekitten.com/700/400?image=6',
'http://placekitten.com/700/400?image=7',
'http://placekitten.com/700/400?image=8',
'http://placekitten.com/700/400?image=9',
'http://placekitten.com/700/400?image=10',
'http://placekitten.com/700/400?image=11',
'http://placekitten.com/700/400?image=12',
];
app.listen(70); // listen on port 70
app.register('.html', doT); // use doT to render templates
app.set('view options', {layout:false}); // keep it simple
doT.templateSettings.strip=false; // don't strip line endings from template file
app.get('/', function(req, res) {
res.render('index.html', { slide: slide, slides: slides });
});
app.post('/next', function(req, res) {
next();
res.send(204); // No Content
});
setInterval(next, 4000); // advance slides every 4 seconds
function next() {
if (++slide >= slides.length) slide = 0;
io.sockets.emit('slide', slide);
}
views/index.html
This file is processed as a doT template.
<!DOCTYPE html>
<html>
<head>
<title>Synchronized Slideshow</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var curslide = {{=it.slide}}; // the slide the server is currently on.
$(function() {
$('#slide' + curslide).css('left',0);
$('#next').click(function() {
$.post('/next');
});
});
var socket = io.connect('http://localhost:70');
socket.on('slide', function(slide) {
$('#slide' + curslide).animate({left:-700}, 400);
$('#slide' + slide).css('left',700).show().animate({left:0}, 400);
curslide = slide;
});
</script>
<style>
#slideshow, .slide { width:700px; height:400px; overflow:hidden; position:relative; }
.slide { position:absolute; top:0px; left:700px; }
</style>
</head>
<body>
<div id="slideshow">
{{~it.slides :url:i}}
<div id="slide{{=i}}" class="slide"><img src="{{=url}}"></div>
{{~}}
</div>
<button id="next">Next ></button>
</body>
</html>
Copy these two files into a folder, then run
$ npm install express socket.io dot
$ node app.js
and navigate to http://localhost:70
in several different windows, then see the magic.
edited Jul 2 '12 at 22:07
answered May 19 '12 at 4:16
josh3736
102k20174225
102k20174225
Love this answer! Ignoring my question to get to the same objective. I'm going to take this approach and see what I can get out of it!
– Josh Hunt
May 20 '12 at 2:27
And kudos for the demo link!
– Josh Hunt
May 20 '12 at 2:33
I got a couple of errors running this with the latest version of Express. Only when I pinned it to version 2.5.10 and reinstalled did it work. Also, I had to runsudo node app.js
– skube
Dec 30 '16 at 15:27
1
Yeah, this is very old and express obviously has many breaking changes between v2 and v4. The need forsudo
comes from listening on port 70, change that to something >1024 to fix.
– josh3736
Jan 2 '17 at 2:28
add a comment |
Love this answer! Ignoring my question to get to the same objective. I'm going to take this approach and see what I can get out of it!
– Josh Hunt
May 20 '12 at 2:27
And kudos for the demo link!
– Josh Hunt
May 20 '12 at 2:33
I got a couple of errors running this with the latest version of Express. Only when I pinned it to version 2.5.10 and reinstalled did it work. Also, I had to runsudo node app.js
– skube
Dec 30 '16 at 15:27
1
Yeah, this is very old and express obviously has many breaking changes between v2 and v4. The need forsudo
comes from listening on port 70, change that to something >1024 to fix.
– josh3736
Jan 2 '17 at 2:28
Love this answer! Ignoring my question to get to the same objective. I'm going to take this approach and see what I can get out of it!
– Josh Hunt
May 20 '12 at 2:27
Love this answer! Ignoring my question to get to the same objective. I'm going to take this approach and see what I can get out of it!
– Josh Hunt
May 20 '12 at 2:27
And kudos for the demo link!
– Josh Hunt
May 20 '12 at 2:33
And kudos for the demo link!
– Josh Hunt
May 20 '12 at 2:33
I got a couple of errors running this with the latest version of Express. Only when I pinned it to version 2.5.10 and reinstalled did it work. Also, I had to run
sudo node app.js
– skube
Dec 30 '16 at 15:27
I got a couple of errors running this with the latest version of Express. Only when I pinned it to version 2.5.10 and reinstalled did it work. Also, I had to run
sudo node app.js
– skube
Dec 30 '16 at 15:27
1
1
Yeah, this is very old and express obviously has many breaking changes between v2 and v4. The need for
sudo
comes from listening on port 70, change that to something >1024 to fix.– josh3736
Jan 2 '17 at 2:28
Yeah, this is very old and express obviously has many breaking changes between v2 and v4. The need for
sudo
comes from listening on port 70, change that to something >1024 to fix.– josh3736
Jan 2 '17 at 2:28
add a comment |
Measure the elapsed time between sending the request and getting back the response. Then, divide that value by 2. That gives you a rough value of one-way latency. If you add that to the time value from the server, you'll be closer to the true server time.
Something like this should work:
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
var start = (new Date).getTime();
r.open('HEAD', document.location, false);
r.onreadystatechange = function()
{
if (r.readyState != 4)
{
return;
}
var latency = (new Date).getTime() - start;
var timestring = r.getResponseHeader("DATE");
// Set the time to the **slightly old** date sent from the
// server, then adjust it to a good estimate of what the
// server time is **right now**.
systemtime = new Date(timestring);
systemtime.setMilliseconds(systemtime.getMilliseconds() + (latency / 2))
};
r.send(null);
}
Interesting aside: John Resig has a good article on the accuracy of Javascript timing.
It shouldn't cause a problem in this case, since you're only concerned about your time being off by ~1 second. A 15 ms difference shouldn't have much effect.
An amazing answer! Pretty sure this is the best solution available, but im going to add a bounty on to see if I can attract some other solutions.
– Josh Hunt
May 16 '12 at 8:14
4
This is how I would do it. The only difference being that I would do a sync call several times (i.e. 10 times) and then use the systemtime from the call with the lowest latency. Indeed, the higher the latency, the more impact you have from the fact that the distribution between from-client-to-server-time and from-server-to-client-time is not exactly 1:1.
– Willem Mulder
May 19 '12 at 12:50
Is there a better option than dividing response time by 2? In some of my tests the server blocks for awhile but then returns quickly. So maybe the trip time was 200ms, but the time returned is only 20ms off. Does that make sense?
– jocull
Jul 23 '13 at 15:27
add a comment |
Measure the elapsed time between sending the request and getting back the response. Then, divide that value by 2. That gives you a rough value of one-way latency. If you add that to the time value from the server, you'll be closer to the true server time.
Something like this should work:
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
var start = (new Date).getTime();
r.open('HEAD', document.location, false);
r.onreadystatechange = function()
{
if (r.readyState != 4)
{
return;
}
var latency = (new Date).getTime() - start;
var timestring = r.getResponseHeader("DATE");
// Set the time to the **slightly old** date sent from the
// server, then adjust it to a good estimate of what the
// server time is **right now**.
systemtime = new Date(timestring);
systemtime.setMilliseconds(systemtime.getMilliseconds() + (latency / 2))
};
r.send(null);
}
Interesting aside: John Resig has a good article on the accuracy of Javascript timing.
It shouldn't cause a problem in this case, since you're only concerned about your time being off by ~1 second. A 15 ms difference shouldn't have much effect.
An amazing answer! Pretty sure this is the best solution available, but im going to add a bounty on to see if I can attract some other solutions.
– Josh Hunt
May 16 '12 at 8:14
4
This is how I would do it. The only difference being that I would do a sync call several times (i.e. 10 times) and then use the systemtime from the call with the lowest latency. Indeed, the higher the latency, the more impact you have from the fact that the distribution between from-client-to-server-time and from-server-to-client-time is not exactly 1:1.
– Willem Mulder
May 19 '12 at 12:50
Is there a better option than dividing response time by 2? In some of my tests the server blocks for awhile but then returns quickly. So maybe the trip time was 200ms, but the time returned is only 20ms off. Does that make sense?
– jocull
Jul 23 '13 at 15:27
add a comment |
Measure the elapsed time between sending the request and getting back the response. Then, divide that value by 2. That gives you a rough value of one-way latency. If you add that to the time value from the server, you'll be closer to the true server time.
Something like this should work:
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
var start = (new Date).getTime();
r.open('HEAD', document.location, false);
r.onreadystatechange = function()
{
if (r.readyState != 4)
{
return;
}
var latency = (new Date).getTime() - start;
var timestring = r.getResponseHeader("DATE");
// Set the time to the **slightly old** date sent from the
// server, then adjust it to a good estimate of what the
// server time is **right now**.
systemtime = new Date(timestring);
systemtime.setMilliseconds(systemtime.getMilliseconds() + (latency / 2))
};
r.send(null);
}
Interesting aside: John Resig has a good article on the accuracy of Javascript timing.
It shouldn't cause a problem in this case, since you're only concerned about your time being off by ~1 second. A 15 ms difference shouldn't have much effect.
Measure the elapsed time between sending the request and getting back the response. Then, divide that value by 2. That gives you a rough value of one-way latency. If you add that to the time value from the server, you'll be closer to the true server time.
Something like this should work:
function syncTime() {
// Set up our time object, synced by the HTTP DATE header
// Fetch the page over JS to get just the headers
console.log("syncing time")
var r = new XMLHttpRequest();
var start = (new Date).getTime();
r.open('HEAD', document.location, false);
r.onreadystatechange = function()
{
if (r.readyState != 4)
{
return;
}
var latency = (new Date).getTime() - start;
var timestring = r.getResponseHeader("DATE");
// Set the time to the **slightly old** date sent from the
// server, then adjust it to a good estimate of what the
// server time is **right now**.
systemtime = new Date(timestring);
systemtime.setMilliseconds(systemtime.getMilliseconds() + (latency / 2))
};
r.send(null);
}
Interesting aside: John Resig has a good article on the accuracy of Javascript timing.
It shouldn't cause a problem in this case, since you're only concerned about your time being off by ~1 second. A 15 ms difference shouldn't have much effect.
edited May 14 '12 at 22:55
Josh Hunt
4,089236792
4,089236792
answered May 14 '12 at 15:17
MikeWyatt
4,91683967
4,91683967
An amazing answer! Pretty sure this is the best solution available, but im going to add a bounty on to see if I can attract some other solutions.
– Josh Hunt
May 16 '12 at 8:14
4
This is how I would do it. The only difference being that I would do a sync call several times (i.e. 10 times) and then use the systemtime from the call with the lowest latency. Indeed, the higher the latency, the more impact you have from the fact that the distribution between from-client-to-server-time and from-server-to-client-time is not exactly 1:1.
– Willem Mulder
May 19 '12 at 12:50
Is there a better option than dividing response time by 2? In some of my tests the server blocks for awhile but then returns quickly. So maybe the trip time was 200ms, but the time returned is only 20ms off. Does that make sense?
– jocull
Jul 23 '13 at 15:27
add a comment |
An amazing answer! Pretty sure this is the best solution available, but im going to add a bounty on to see if I can attract some other solutions.
– Josh Hunt
May 16 '12 at 8:14
4
This is how I would do it. The only difference being that I would do a sync call several times (i.e. 10 times) and then use the systemtime from the call with the lowest latency. Indeed, the higher the latency, the more impact you have from the fact that the distribution between from-client-to-server-time and from-server-to-client-time is not exactly 1:1.
– Willem Mulder
May 19 '12 at 12:50
Is there a better option than dividing response time by 2? In some of my tests the server blocks for awhile but then returns quickly. So maybe the trip time was 200ms, but the time returned is only 20ms off. Does that make sense?
– jocull
Jul 23 '13 at 15:27
An amazing answer! Pretty sure this is the best solution available, but im going to add a bounty on to see if I can attract some other solutions.
– Josh Hunt
May 16 '12 at 8:14
An amazing answer! Pretty sure this is the best solution available, but im going to add a bounty on to see if I can attract some other solutions.
– Josh Hunt
May 16 '12 at 8:14
4
4
This is how I would do it. The only difference being that I would do a sync call several times (i.e. 10 times) and then use the systemtime from the call with the lowest latency. Indeed, the higher the latency, the more impact you have from the fact that the distribution between from-client-to-server-time and from-server-to-client-time is not exactly 1:1.
– Willem Mulder
May 19 '12 at 12:50
This is how I would do it. The only difference being that I would do a sync call several times (i.e. 10 times) and then use the systemtime from the call with the lowest latency. Indeed, the higher the latency, the more impact you have from the fact that the distribution between from-client-to-server-time and from-server-to-client-time is not exactly 1:1.
– Willem Mulder
May 19 '12 at 12:50
Is there a better option than dividing response time by 2? In some of my tests the server blocks for awhile but then returns quickly. So maybe the trip time was 200ms, but the time returned is only 20ms off. Does that make sense?
– jocull
Jul 23 '13 at 15:27
Is there a better option than dividing response time by 2? In some of my tests the server blocks for awhile but then returns quickly. So maybe the trip time was 200ms, but the time returned is only 20ms off. Does that make sense?
– jocull
Jul 23 '13 at 15:27
add a comment |
I'm glad you found a satisfactory answer to your question. I had a similar need to synchronize the browser with the server's clock and was determined to achieve it with better than 1 second accuracy like you were. I've written code to do this and am posting this answer here in case anyone else needs the solution too.
The code is called ServerDate and is freely available for download. Here's part of the README. Notice that I achieved 108 ms precision in my example:
You can use ServerDate
as you would use the Date
function or one of its
instances, e.g.:
> ServerDate()
"Mon Aug 13 2012 20:26:34 GMT-0300 (ART)"
> ServerDate.now()
1344900478753
> ServerDate.getMilliseconds()
22
There is also a new method to get the precision of ServerDate's estimate of the
server's clock (in milliseconds):
> ServerDate.toLocaleString() + " ± " + ServerDate.getPrecision() + " ms"
"Tue Aug 14 01:01:49 2012 ± 108 ms"
You can see the difference between the server's clock and the browsers clock, in milliseconds:
> ServerDate - new Date()
39
add a comment |
I'm glad you found a satisfactory answer to your question. I had a similar need to synchronize the browser with the server's clock and was determined to achieve it with better than 1 second accuracy like you were. I've written code to do this and am posting this answer here in case anyone else needs the solution too.
The code is called ServerDate and is freely available for download. Here's part of the README. Notice that I achieved 108 ms precision in my example:
You can use ServerDate
as you would use the Date
function or one of its
instances, e.g.:
> ServerDate()
"Mon Aug 13 2012 20:26:34 GMT-0300 (ART)"
> ServerDate.now()
1344900478753
> ServerDate.getMilliseconds()
22
There is also a new method to get the precision of ServerDate's estimate of the
server's clock (in milliseconds):
> ServerDate.toLocaleString() + " ± " + ServerDate.getPrecision() + " ms"
"Tue Aug 14 01:01:49 2012 ± 108 ms"
You can see the difference between the server's clock and the browsers clock, in milliseconds:
> ServerDate - new Date()
39
add a comment |
I'm glad you found a satisfactory answer to your question. I had a similar need to synchronize the browser with the server's clock and was determined to achieve it with better than 1 second accuracy like you were. I've written code to do this and am posting this answer here in case anyone else needs the solution too.
The code is called ServerDate and is freely available for download. Here's part of the README. Notice that I achieved 108 ms precision in my example:
You can use ServerDate
as you would use the Date
function or one of its
instances, e.g.:
> ServerDate()
"Mon Aug 13 2012 20:26:34 GMT-0300 (ART)"
> ServerDate.now()
1344900478753
> ServerDate.getMilliseconds()
22
There is also a new method to get the precision of ServerDate's estimate of the
server's clock (in milliseconds):
> ServerDate.toLocaleString() + " ± " + ServerDate.getPrecision() + " ms"
"Tue Aug 14 01:01:49 2012 ± 108 ms"
You can see the difference between the server's clock and the browsers clock, in milliseconds:
> ServerDate - new Date()
39
I'm glad you found a satisfactory answer to your question. I had a similar need to synchronize the browser with the server's clock and was determined to achieve it with better than 1 second accuracy like you were. I've written code to do this and am posting this answer here in case anyone else needs the solution too.
The code is called ServerDate and is freely available for download. Here's part of the README. Notice that I achieved 108 ms precision in my example:
You can use ServerDate
as you would use the Date
function or one of its
instances, e.g.:
> ServerDate()
"Mon Aug 13 2012 20:26:34 GMT-0300 (ART)"
> ServerDate.now()
1344900478753
> ServerDate.getMilliseconds()
22
There is also a new method to get the precision of ServerDate's estimate of the
server's clock (in milliseconds):
> ServerDate.toLocaleString() + " ± " + ServerDate.getPrecision() + " ms"
"Tue Aug 14 01:01:49 2012 ± 108 ms"
You can see the difference between the server's clock and the browsers clock, in milliseconds:
> ServerDate - new Date()
39
edited Dec 11 '13 at 19:32
answered Aug 14 '12 at 15:57
David Braun
2,91812237
2,91812237
add a comment |
add a comment |
You can't really sync up with the server. Measuring the time your server request needs (as MikeWyatt suggested) is not a good indicator on the latency.
Only your server knows when he responds a request. Therefore, it should send that information back with the answer. With Date.now() - new Date(timestringOfServerResponse)
you can measure the latency exactly. Yet I'm not sure why you would need that value.
To sync an app between mulitiple devices, the server should send them which action to perform when. The "when" should not be "as soon as you get my response", but a exact timestamp. As far as the system clocks of your devices are accurate and synced (they usually are), the app will run its methods synchrounously, because it knows what to happen when (or at least: what should have happened then, and it can interpolate what to happen "now").
1
That's exactly how this is functioning. The page waits until the correct time before performing an action to ensure it is syncd across devices. The problem comes down to the fact that I have two iPads next to each other which are set to sync time with time servers, yet are 1 second apart.
– Josh Hunt
May 15 '12 at 11:20
So you want to implement a time server in javascript, to sync "custom system times"?
– Bergi
May 15 '12 at 11:25
2
@Bergi, you're erroneously assuming that the client's clock matches the clock on the server. They are practically guaranteed to be different, probably by multiple seconds or minutes. Adjusting for latency isn't perfect, but it will get you pretty close. In the worst case, you'll be off by the full round-trip time, if one of the trips was instantaneous.
– MikeWyatt
May 16 '12 at 3:47
add a comment |
You can't really sync up with the server. Measuring the time your server request needs (as MikeWyatt suggested) is not a good indicator on the latency.
Only your server knows when he responds a request. Therefore, it should send that information back with the answer. With Date.now() - new Date(timestringOfServerResponse)
you can measure the latency exactly. Yet I'm not sure why you would need that value.
To sync an app between mulitiple devices, the server should send them which action to perform when. The "when" should not be "as soon as you get my response", but a exact timestamp. As far as the system clocks of your devices are accurate and synced (they usually are), the app will run its methods synchrounously, because it knows what to happen when (or at least: what should have happened then, and it can interpolate what to happen "now").
1
That's exactly how this is functioning. The page waits until the correct time before performing an action to ensure it is syncd across devices. The problem comes down to the fact that I have two iPads next to each other which are set to sync time with time servers, yet are 1 second apart.
– Josh Hunt
May 15 '12 at 11:20
So you want to implement a time server in javascript, to sync "custom system times"?
– Bergi
May 15 '12 at 11:25
2
@Bergi, you're erroneously assuming that the client's clock matches the clock on the server. They are practically guaranteed to be different, probably by multiple seconds or minutes. Adjusting for latency isn't perfect, but it will get you pretty close. In the worst case, you'll be off by the full round-trip time, if one of the trips was instantaneous.
– MikeWyatt
May 16 '12 at 3:47
add a comment |
You can't really sync up with the server. Measuring the time your server request needs (as MikeWyatt suggested) is not a good indicator on the latency.
Only your server knows when he responds a request. Therefore, it should send that information back with the answer. With Date.now() - new Date(timestringOfServerResponse)
you can measure the latency exactly. Yet I'm not sure why you would need that value.
To sync an app between mulitiple devices, the server should send them which action to perform when. The "when" should not be "as soon as you get my response", but a exact timestamp. As far as the system clocks of your devices are accurate and synced (they usually are), the app will run its methods synchrounously, because it knows what to happen when (or at least: what should have happened then, and it can interpolate what to happen "now").
You can't really sync up with the server. Measuring the time your server request needs (as MikeWyatt suggested) is not a good indicator on the latency.
Only your server knows when he responds a request. Therefore, it should send that information back with the answer. With Date.now() - new Date(timestringOfServerResponse)
you can measure the latency exactly. Yet I'm not sure why you would need that value.
To sync an app between mulitiple devices, the server should send them which action to perform when. The "when" should not be "as soon as you get my response", but a exact timestamp. As far as the system clocks of your devices are accurate and synced (they usually are), the app will run its methods synchrounously, because it knows what to happen when (or at least: what should have happened then, and it can interpolate what to happen "now").
answered May 14 '12 at 23:14
Bergi
362k57538861
362k57538861
1
That's exactly how this is functioning. The page waits until the correct time before performing an action to ensure it is syncd across devices. The problem comes down to the fact that I have two iPads next to each other which are set to sync time with time servers, yet are 1 second apart.
– Josh Hunt
May 15 '12 at 11:20
So you want to implement a time server in javascript, to sync "custom system times"?
– Bergi
May 15 '12 at 11:25
2
@Bergi, you're erroneously assuming that the client's clock matches the clock on the server. They are practically guaranteed to be different, probably by multiple seconds or minutes. Adjusting for latency isn't perfect, but it will get you pretty close. In the worst case, you'll be off by the full round-trip time, if one of the trips was instantaneous.
– MikeWyatt
May 16 '12 at 3:47
add a comment |
1
That's exactly how this is functioning. The page waits until the correct time before performing an action to ensure it is syncd across devices. The problem comes down to the fact that I have two iPads next to each other which are set to sync time with time servers, yet are 1 second apart.
– Josh Hunt
May 15 '12 at 11:20
So you want to implement a time server in javascript, to sync "custom system times"?
– Bergi
May 15 '12 at 11:25
2
@Bergi, you're erroneously assuming that the client's clock matches the clock on the server. They are practically guaranteed to be different, probably by multiple seconds or minutes. Adjusting for latency isn't perfect, but it will get you pretty close. In the worst case, you'll be off by the full round-trip time, if one of the trips was instantaneous.
– MikeWyatt
May 16 '12 at 3:47
1
1
That's exactly how this is functioning. The page waits until the correct time before performing an action to ensure it is syncd across devices. The problem comes down to the fact that I have two iPads next to each other which are set to sync time with time servers, yet are 1 second apart.
– Josh Hunt
May 15 '12 at 11:20
That's exactly how this is functioning. The page waits until the correct time before performing an action to ensure it is syncd across devices. The problem comes down to the fact that I have two iPads next to each other which are set to sync time with time servers, yet are 1 second apart.
– Josh Hunt
May 15 '12 at 11:20
So you want to implement a time server in javascript, to sync "custom system times"?
– Bergi
May 15 '12 at 11:25
So you want to implement a time server in javascript, to sync "custom system times"?
– Bergi
May 15 '12 at 11:25
2
2
@Bergi, you're erroneously assuming that the client's clock matches the clock on the server. They are practically guaranteed to be different, probably by multiple seconds or minutes. Adjusting for latency isn't perfect, but it will get you pretty close. In the worst case, you'll be off by the full round-trip time, if one of the trips was instantaneous.
– MikeWyatt
May 16 '12 at 3:47
@Bergi, you're erroneously assuming that the client's clock matches the clock on the server. They are practically guaranteed to be different, probably by multiple seconds or minutes. Adjusting for latency isn't perfect, but it will get you pretty close. In the worst case, you'll be off by the full round-trip time, if one of the trips was instantaneous.
– MikeWyatt
May 16 '12 at 3:47
add a comment |
I'm extensively using the COMET pattern here for my real time web application.
To use that in your case you'd need the clients to open an AJAX request to the server and wait for an answer. As soon as it comes the client has to change slides.
On the server you have to hold back all answers till it's time to change slides. (You could be more advanced and delay afterwards on the client for the same time each, but that's most likely not necessary). I can't show you sample code for that here as I don't know what's available to you.
So you are effectively creating an orchestra where the server plays the conductor and all clients are listening to him.
Timing is then determined by the ability of the server to answer the requests at (nearly) the same time plus the network latency.
Usually the clients should be in the same part of the network so latency might be very similar - and the absolute value doesn't hurt here, only the variation.
And there might also be an additional trick helping: don't change the slides with a hard replacement, blend them. This will blur the change so that the eye can't catch the little timing differences that you'll always have.
(If you can't have the server playing conductor you're likely to have to use the solution by MikeWyatt - probably with a few requests and averaging the result, depending on the network setup. In a LAN one request might be enough, going over the full internet a bit over averaging won't hurt...)
add a comment |
I'm extensively using the COMET pattern here for my real time web application.
To use that in your case you'd need the clients to open an AJAX request to the server and wait for an answer. As soon as it comes the client has to change slides.
On the server you have to hold back all answers till it's time to change slides. (You could be more advanced and delay afterwards on the client for the same time each, but that's most likely not necessary). I can't show you sample code for that here as I don't know what's available to you.
So you are effectively creating an orchestra where the server plays the conductor and all clients are listening to him.
Timing is then determined by the ability of the server to answer the requests at (nearly) the same time plus the network latency.
Usually the clients should be in the same part of the network so latency might be very similar - and the absolute value doesn't hurt here, only the variation.
And there might also be an additional trick helping: don't change the slides with a hard replacement, blend them. This will blur the change so that the eye can't catch the little timing differences that you'll always have.
(If you can't have the server playing conductor you're likely to have to use the solution by MikeWyatt - probably with a few requests and averaging the result, depending on the network setup. In a LAN one request might be enough, going over the full internet a bit over averaging won't hurt...)
add a comment |
I'm extensively using the COMET pattern here for my real time web application.
To use that in your case you'd need the clients to open an AJAX request to the server and wait for an answer. As soon as it comes the client has to change slides.
On the server you have to hold back all answers till it's time to change slides. (You could be more advanced and delay afterwards on the client for the same time each, but that's most likely not necessary). I can't show you sample code for that here as I don't know what's available to you.
So you are effectively creating an orchestra where the server plays the conductor and all clients are listening to him.
Timing is then determined by the ability of the server to answer the requests at (nearly) the same time plus the network latency.
Usually the clients should be in the same part of the network so latency might be very similar - and the absolute value doesn't hurt here, only the variation.
And there might also be an additional trick helping: don't change the slides with a hard replacement, blend them. This will blur the change so that the eye can't catch the little timing differences that you'll always have.
(If you can't have the server playing conductor you're likely to have to use the solution by MikeWyatt - probably with a few requests and averaging the result, depending on the network setup. In a LAN one request might be enough, going over the full internet a bit over averaging won't hurt...)
I'm extensively using the COMET pattern here for my real time web application.
To use that in your case you'd need the clients to open an AJAX request to the server and wait for an answer. As soon as it comes the client has to change slides.
On the server you have to hold back all answers till it's time to change slides. (You could be more advanced and delay afterwards on the client for the same time each, but that's most likely not necessary). I can't show you sample code for that here as I don't know what's available to you.
So you are effectively creating an orchestra where the server plays the conductor and all clients are listening to him.
Timing is then determined by the ability of the server to answer the requests at (nearly) the same time plus the network latency.
Usually the clients should be in the same part of the network so latency might be very similar - and the absolute value doesn't hurt here, only the variation.
And there might also be an additional trick helping: don't change the slides with a hard replacement, blend them. This will blur the change so that the eye can't catch the little timing differences that you'll always have.
(If you can't have the server playing conductor you're likely to have to use the solution by MikeWyatt - probably with a few requests and averaging the result, depending on the network setup. In a LAN one request might be enough, going over the full internet a bit over averaging won't hurt...)
answered May 19 '12 at 12:20
Chris
1,50532745
1,50532745
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f10585910%2fsync-js-time-between-multiple-devices%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown