DEV Community

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 (11)

Collapse
kenbellows profile image
Ken Bellows

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!

Collapse
nataliedeweerd profile image
𝐍𝐚𝐭𝐚π₯𝐒𝐞 𝐝𝐞 π–πžπžπ«π Author • Edited

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 :)

Collapse
kallmanation profile image
Nathan Kallman

Sorry to necro; but I couldn't resist throwing my 2 cents at it!

I would add a table to track the "big screen" 's current image (a row for each "big screen", with a foreign key to the images table you have above):

ID CurrentImageID Name
1 3 Singapore
2 2 London

Then there's just a few operations to implement:

  1. User uploads new photo: just insert another record in the Images table
  2. "Big Screen" needs a new photo to display: check if that big screen's CurrentImageID is less than the largest ID in the Images table
    • if it is: display the image CurrentImageID points to, and increment CurrentImageID to the next one
    • if it is not (aka users have stopped uploading pictures for awhile): return a random image and do not increment CurrentImageID
  3. Adding a new "Big Screen"? Insert another row to this table and set CurrentImageID to the most recent image.
Collapse
nataliedeweerd profile image
𝐍𝐚𝐭𝐚π₯𝐒𝐞 𝐝𝐞 π–πžπžπ«π Author

Great solution ! Thanks for taking the time to share it :D Don't worry about necroing the thread, it's great to get several different perspectives, as I know a similar job will come around again when we can travel more freely!

How would you trigger the "Big Screen" image check? CRON? OR just "listen" for an image upload?

Collapse
kallmanation profile image
Nathan Kallman

If I'm limited to just HTTP (so no websockets, server push schemes, or custom protocols) then I would just have the "Big Screen" regularly poll (aka cron) for a new image (maybe 10 seconds?).

If we can use websockets or similar, then that opens the door for fun things of the server notifying screens that new images are available (but then the screen would still make the "request" to get that new image so that it always goes through the same consistent process)

Collapse
matthewdunbar1 profile image
Matthew Dunbar

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.

Collapse
nataliedeweerd profile image
𝐍𝐚𝐭𝐚π₯𝐒𝐞 𝐝𝐞 π–πžπžπ«π Author

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.

Collapse
nataliedeweerd profile image
𝐍𝐚𝐭𝐚π₯𝐒𝐞 𝐝𝐞 π–πžπžπ«π Author

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.

Collapse
ahferroin7 profile image
Austin S. Hemmelgarn

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.

Collapse
nataliedeweerd profile image
𝐍𝐚𝐭𝐚π₯𝐒𝐞 𝐝𝐞 π–πžπžπ«π Author

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!

Forem Open with the Forem app