DEV Community

Cover image for Retro CRT terminal screen in CSS + JS

Retro CRT terminal screen in CSS + JS

Edwin on February 21, 2020

As a fun project, I decided to create a terminal interface that has the look and feel of an old CRT monitor. The terminal is responsive, but it m...
Collapse
 
maveric profile image
maveric

Edwin,

I love this. By far the most in-depth, well written fallout game (as well as the terminal, but I found it looking for the fallout game) I've seen yet. The fact that it's in such an amazing package of the terminal with scanlines, typing, other command framework. It's great.

I'd like to use this for our airsoft events that we host (you read that right) and would like to know how to properly attribute you. I also have a question about why / how to fix - in the fallout game, when the typer is updating the lives (updateLives()) and you move the cursor it cancels the typer and can sometimes mess up the ending of the game (assuming waiting for a promise that gets wonkey?)

If you're willing, let me know your thoughts on the typer, but please let me know how to best give you credit to our players.

Collapse
 
ekeijl profile image
Edwin

Hey, thanks man! I'm really happy to hear that other people use my code for all kinds of stuff! I have not really thought about how to give credit, maybe just link to my dev.to profile for now. I was working on a portfolio site, this might be the motivation to actually finish it. :)

About the updateLives function, I assume you want to wait for the updateLives function to finish before doing the next thing. I see that this function does not return anything, that may cause the issue?

async function updateLives() {
    let span = document.querySelector(".lives");

    let blocks = Array(lives)
        .fill(0)
        .map(() => "■ ")
        .join("");

        // Added return here
    return await type("Text", { clearContainer: true }, span);
  }
Enter fullscreen mode Exit fullscreen mode

Then you can wait for the function to finish by using async/await:

async function doStuff() {

  //...
  await updateLIves();
  // should wait for updateLives to finish
Enter fullscreen mode Exit fullscreen mode
Collapse
 
maveric profile image
maveric

I'm not sure if you are still reading comments on this article but we had our airsoft event last month and these were a HIT. I loaded it up on some rPi's and battery packs with wifi that redirected all traffic to the pi.

I built a local backend API that took in the "command" and tested it against our list and sent back either brogue or fallout to run based on our new command names. Then hooked into the win/losses and sent that back to the api so we could have dynamic win/loss messages based on the game that was played. It added something completely new to the airsoft world and we will absolutely be bringing it back in future games.

Thread Thread
 
ekeijl profile image
Edwin

That's so cool! I picture this as a "retro arcade" that you use during the downtime of the airsoft event? Thanks for the update!

Thread Thread
 
maveric profile image
maveric

Nope. :P We've got the pi's people connect with with their phone and a few toughbooks they've got to be in front of. You've got to have people pull security (to keep enemies away) or play the game under fire in order to get the information out of it to complete your mission and keep things progressing. The "hacking terminal" is very much a part of the live fire scenario game.

Thread Thread
 
ekeijl profile image
Edwin

That is freakin' genius! Sort of "hacking" mini-games during the event, that's so creative! Reminds me of this "scavenger hunt" game that we used to play on my university campus.

I'm currently playing a game called "Control" which also has some terminal based mini-games, might add it to this project. (no promises tho)

Collapse
 
maveric profile image
maveric • Edited

I've made that change, and the execution does not seem to change. I have a "console.log" immediately following the call to updateLives that in fact will not show until it completes. But if I move the mouse, the

wordSpan.addEventListener("mouseenter", handleWordHover);

gets called (as it's on all elements) and seems to interrupt the promise. If I move the mouse, I never get that console.log statement for that call. If that game ends with that interrupted, it won't show the game end typer properly.

I have not been able to find any resources on this type of interaction - an event listener popping off during an await that messes it up - and this whole async/await was not a paradigm when I was coding last. Trying to catch up.

Your help/suggestions would be greatly appreciated but I know this was just a side/learning project, not something you intended to support and won't be offended if you chose not to.

Thread Thread
 
ekeijl profile image
Edwin

If I could look at the code somehow I can help debugging. Can you put it online somewhere like a codesandbox?

Thread Thread
 
maveric profile image
maveric • Edited

The sandbox is at:
codesandbox.io/s/hungry-booth-wyl1...
It really is just your code that I've made some minor bug fixes and personal debug/figure out code to. The same thing happens on the sandbox from this write up as well if it's easier to look at code I haven't put any hack-it-to-figure-it-out code into.

Thread Thread
 
ekeijl profile image
Edwin • Edited

Oh, it was easier than I thought.

I defined the interval variable at the top of the io.js module. Everytime you call type() it will overwrite the interval. I use setInterval to process the queue of characters one by one. So if type() is called while another is still running, only the last one will finish.

Moving the interval variable inside the type() function ensures every promise will execute asynchronously like you expect. Not sure why I wrote it like this in the first place, probably because I thought I would only need 1 typer at the same time, lol.

Thread Thread
 
maveric profile image
maveric

That's fantastic. I'm very glad it was an easy find for you and greatly appreciate you looking into it for me. Works like a charm. That would have taken me ages to find, as I was going down the "how's the promise getting messed up" path.

I've got a lot to learn, but you've got great code for me to follow, so thanks again.

Collapse
 
chafalleiro profile image
Chafalleiro

Hello I borrowed your code, also added some SVG effect to do a barrel distortion of the screen that you can find interesting. Credited in the credits menu, I hope the crediting is correct.

chafalleiro.github.io/retromator/v...

Collapse
 
ekeijl profile image
Edwin

This looks perfect! 🤩

Collapse
 
chafalleiro profile image
Chafalleiro

Thank you very much :) Certainly you post helped me a lot.

Collapse
 
fhgaha profile image
fhgaha • Edited

im trying to repeat. almost done but cant find what "sphere_wide_1.png" look like

Collapse
 
hbaguette profile image
H-Baguette

Hey! I'm trying to use this code to make a small terminal for a game, but... for whatever reason, it's saying the :after elements are invalid names, and thus none of it shows up. I'm also having an issue of my scanlines appearing over my border image.

Collapse
 
ekeijl profile image
Edwin

Can you show me the code somehow? Codesandbox or github? I'm not sure what you mean.

Collapse
 
hbaguette profile image
H-Baguette

I managed to fix most of those issues, plus a few more I was having after adapting it for my own purposes, but I'm still having the issue with the scanlines appearing over the corners of the border image. The code's probably gonna look confusing and spaghettified to all hell, but I can add you to the Github repo, if you want to take a look.

Collapse
 
sungbinlee profile image
Sungbin Lee

Hey Edwin, Just wanted to say a big thank you for your amazing article. Thanks for sharing your knowledge. It's been a huge inspiration for my project! github.com/sungbinlee/TicTacToeWit...

Collapse
 
ekeijl profile image
Edwin

Hello Sungbin Lee, that's a cool project, glad I could be of help! :D

Collapse
 
rizpng profile image
RIZ

Hey Edwin,

I'd like to say i love the way you designed your terminal and I'm glad it's being used to help other people. I was just wondering if it's possible to convert the terminal into a text editor tool instead given that I cannot freely text as i receive "unknown command" error whenever i type.

I'm not tech saavy at all so I'm unsure how to utilise Sandbox or any of the other tools used to code. Any feedback would be greatly appreciated

Image description

Collapse
 
ekeijl profile image
Edwin

Hey RIZ, I made a new command text-editor command, it saves what you typed in your browser.

tlijm.csb.app/?command=text-editor

Collapse
 
rizpng profile image
RIZ

yeah, i've been playing around with it and it works perfectly. i appreciate the support edwin and will continue to follow and support your work.
have a nice one mate :)

Collapse
 
doug_davison_2f87696dcf3d profile image
Doug Davison

It would be cool to use this to build various screens for use during an RPG session. I could see a need for building a custom intro screen for each terminal's welcome screen and login, and then menu items that do various things or open log journal records.

  1. Shut off exterior turrets (admin only)
  2. Open valve in backup tank
  3. Read Journal Entries
  4. Hack system to gain admin rights
Collapse
 
fossheim profile image
Sarah

Really love this! 😍

Collapse
 
zcysxy profile image
Chenyu ZHANG

Great article! I can easily follow all the steps. There's only one thing that is I couldn't find the bezel.png, am I missing something?

Collapse
 
zcysxy profile image
Chenyu ZHANG

Oh I found it in the codesandbox! How can I miss that. Thanks!

Collapse
 
fritzvd profile image
Fritz van Deventer • Edited

This is awesome. Thanks for sharing. I totally stole everything from here to create my birthday “invite”: fritzvd.com/33/

Collapse
 
ekeijl profile image
Edwin

Nice!

Collapse
 
fradar profile image
FRADAR

I just love this.

Collapse
 
wordpressure profile image
TricaExMachina

This is gorgeous, what great work.

Collapse
 
junipersaroj profile image
juniperSaroj

how can i log in it is asking for the password and username
i need to use this for some video editing process guess ... please help bro.

Collapse
 
ekeijl profile image
Edwin

Just use "admin" for both username and password, it's in the source code 😆

Collapse
 
bartydash profile image
BartyDash

Great job! It's awesome 😍🤯