Inspiration

After registering the .tv domain with the GoDaddy promo code, I got the idea to make a simple & minimalist web UI frontend for fans to watch my music Twitch stream.

What it does

Provides an embedded Twitch viewer and chat window with responsive design. Minimalist symbolic designed links are provided to access alternate video & audio stream platforms. Kubernetes-style health check endpoints (/livez and /readyz) are implemented as custom routes inside the Go HTTP server.

How we built it

I started with a simple static site and added a GoLang-based simple fileserver that runs inside a Docker container on DigitalOcean. DNS is pointed via CNAME watch.phasik.tv to DigitalOcean. Meanwhile, a top-level temporary redirect currently points phasik.tv directly to watch.phasik.tv. This can be adjusted later to provide a homepage in the future.

Next, I added k8s health check endpoints: /livez and /readyz. These were implemented to return JSON with the HTTP status code and message, with an optional data parameter key. The JSON response handler functions accept a generic status uint16, so they both can be used in the future for other status codes and responses. This was refactored into two generic functions: handleJSONResponse(), and response2JSON(), which transfer the optional data bytes content, and response JSON over a set of io.Pipes. This implementation should allow for easy decoupling and scaling of both sides of the pipe in the future. The current implementation simply sends EOF for the encapsulated data content, and responds with an empty data JSON string "".

In order to avoid a deadlock with the blocking io.Pipe reads, I used a go routine to run response2JSON() in parallel. It was also a great excuse for me to play around with go routines in GoLang for the first time ;-)

Challenges we ran into

  • Responsive Design was difficult
  • Content Security Policy headers prevented iframe load without proper configuration
  • GoLang's static typing made implementing a config file schema a bit difficult

Responsive design for the Twitch embedded iframes was a bit tricky and difficult to get working properly. The default JavaScript embed method that Twitch provides does not allow for easy customization of the generated HTML elements, and also increased the time to "First Meaningful Paint". Static embeds with parent= HTTP GET parameters allowed for the static site to request Content Security Policy for the DNS alternate names (e.g. watch.phasik.tv). I'm not a frontend or UI designer, so the fiddlyness of the CSS and iframes took a bit of time.

In a feature branch, I started working on a YAML config file schema to control which CORS domains to pass in to the Go HTTP server at runtime. The idea was to try and use Go's built-in templating to render the HTML dynamically, or perhaps cache it once at startup and serve this static site HTML for the duration of the container's lifetime. However, I was a bit unfamiliar with Go's Marshal & Unmarshal syntax for complex data structures. This took a lot of time to figure out a basic implementation proof-of-concept, but I still wasn't happy with the result that I did get working. I'll have to revisit this later once I learn more complex data structures in GoLang.

Accomplishments that we're proud of

The woff-based symbolic icons are all font glyphs! The trick was to include a patched NerdFont woff2 with woff fonts as fallback. The UTF-8 characters for each symbol are directly added to the static HTML for each link. This makes it clean & simple when developing with any NerdFont in your IDE and/or terminal (e.g. vim), because the icons show up directly in the text. They are also simple to style with CSS. Contrast this to using svg or img elements, which must be previewed outside the editor, sized, and colored appropriately to fit the site theme.

As my first major Go-based project, this was great learning experience!

Also, while setting up CI/CD using DigitalOcean's doctl, it inspired me to create another pre-commit hook project to help validate the .do/app.yaml App Spec file. I also made contributions to upstream doctl and the official GitHub Action project for doctl to implement a verify-offline feature. (More on that in my next project submissions)

What we learned

  • Getting things up and running with DigitalOcean was much simpler & faster than AWS
  • Web font standards make for simple & minimalist UI design elements
  • CORS is always a headache, but necessary for modern browsers
  • Static typing in Go can be tricky, especially with complex nested data structures

What's next for phasik.tv

  • Add another sidecar nginx container to allow for RTSP push to multiple streaming endpoints, similar to restream.io
  • Add more subdomains & routes to the currently simple Go server
    • Maybe something Web3 / smart-contract based? (Artist NFTs, livestream interactions, etc...?)
    • Some sort of dynamic content, such that Go server is even necessary?
    • Maybe just keep it simple and host as a static site only?
  • Finish implementing support for YAML config file & basic HTTP templating

Built With

Share this project:

Updates