loading...

How would you tackle this? Multiple screens showing the same user generated content in real time.

nataliedeweerd profile image 𝐍𝐚𝐭𝐚π₯𝐒𝐞 𝐝𝐞 π–πžπžπ«π ・2 min read

The Problem

So I had a project recently in which there were two components: the "app", and the "big screen". Users take their photo with the app, apply some fun effects, and then send it to the big screen for the world to see! These "big screens" are always on.

The "app" was being developed by another developer externally. They would be passing data to me to display on the big screen via a custom API. The screen itself was a super simple microsite which rotated through several images until it was interrupted by some user generated content passed to me from the app.

The catch however was that there were multiple locations where these apps and screens could be installed. Wherever a photo was uploaded, it should appear on every screen. So if someone in Singapore took a photo, it would appear on the big screen in London and Singapore.

Basic user journey

  1. User takes photo using app.
  2. User adds some fun effects.
  3. User sends the photo to the big screen.
  4. Big screens all around the world display the photo.
  5. Photo then gets scheduled for deletion.

My Solution

Initially I used just an AJAX call to get the next photograph stored in the database, then set it to "used" so it didn't appear again. However this meant that if Screen A grabbed it before Screen B, Screen B would never see it. Not ideal if the user is waiting in front of Screen B to see themselves!

So I went down a server-side CRON route. Every 10 seconds, my CRON would run and set the status of any current image from 2, to 1; and set any unused image to 2. Below is the CRON script I used to run a command every 10 seconds.

for i in {1..5}; do curl --silent "https://***/api/prep" &>/dev/null; sleep 10; done

My table in my database looked like this:

ID img status
1 e1321fsdffdsfds.jpg 1
2 mpfhmnh80fhj823.jpg 2
3 fdsfhjdskfu8h1u.jpg 0

0 represents an image which hasn't been used yet, and is in essence queued.
1 represents an image which has been used / shown on the big screen.
2 represents the current image to be shown on the screen.

My AJAX call would get the next image where status = 2 and display it.

This works mostly; however there is a tiny chance of the image not appearing on a screen. My CRON runs every 10 seconds. My Javascript AJAX call runs every 10.15 seconds. There's the tiny possibility that an image a user takes does not appear on the screen they're looking at.

How would you have solved this problem?

Very interested to hear how other developers may have tackled this problem! The solution I've presented above is a PHP based solution, with some AJAX/JS thrown in.

I should add this project launched successfully. I'm just looking to open a discussion to discover what other technologies/methods could be used to complete this job.

Discussion

markdown guide
 

I would use WebSockets. Then you don't have to worry about timing or marking photos as used. The PHP microsite would get the photo from the app just as it is now, then the PHP microsite can send the photo out via WebSockets to every screen at the same time.

 

WebSockets! Of course! That's basically what they're designed for right? I'd completely forgotten they were a thing. This is exactly what I wanted to start a discussion like this. I'm the only developer at my company, so I don't have someone I can spitball ideas with; and I guess in this instance I forgot Google existed - haha.

 

Definitely a good case for server-push via WebSockets. I'd make the big screens just dumb clients that connect to the server via WebSocket, listen for images over that socket, and display whatever is recieved. Then whenever the server receives a new user image, it should push it once to all connected big screen clients. Probably no need to even keep the image state around anymore in this architecture.

But that's not to say that your solution was invalid; some platforms still don't have great support for WebSockets (though Socket.io usually takes care of that nicely), and ultimately if your solution worked, then it worked! It's definitely a clever solution!

 

Thank's for your feedback Ken! This is exactly what I hoped to achieve from making this thread. I'd completely forgotten WebSockets were a thing. They're not something I've had to use yet, but you're right this project seems like the perfect time to research how to use them. May even look into refactoring my code; or at least using them the next time a similar project comes along.

and ultimately if your solution worked, then it worked! It's definitely a clever solution!

Thank you! At the end of the day, the client doesn't care what's running on the backend, so long as it works :)

 

I'd structure things with a server-push architecture instead of a client-pull architecture. Such an approach has a couple of specific advantages:

  • It means the 'big screen' component can be tiny. It literally just has to connect to the server and then update the display every time it gets an image. That could probably be done in less than 16kB of minified Javascript with zero external dependencies provided you restrict it to running on a sufficiently new browser.
  • If you ever decide to change the update interval, you only need to update the server side, and the change would take effect for everyone the moment it's deployed on the server.
  • Because the server is picking and sending the images, there's no chance of the client not getting any image (you mentioned you were using SQL, you can actually achieve this there too by grouping the state updates into a single transaction, though in that case some clients might miss images instead of just getting nothing back).
  • Because the server is pushing things out itself, it's easier to scale by fanning out through proxies. An architecture like this can easily be made to support anycast addressing too.

As far as how to do it, there are literally dozens of options. I'd probably go with a PWA and use the Push API from the app's service worker. From there, it just amounts to a bit of extra server-side logic to actually push things out.

 

Thanks for your suggestions Austin!

Luckily we can control which browser the "big screen" will use. As it won't be something which is launched to the wide world to access, we can ensure we use newer browsers. So thankfully we're open to new technologies!

 

Thanks Neil! Definitely going to research more into WebSockets now and create this project again using them. May refactor the code to include them, but at the very least I'll have a new version the next time a similar project comes along.