Friend, here's a new old experiment π regular livecoding. It's like a podcast with video, show notes, regular cadence, and we both learn something new.
For me it's a regular timebox βΒ Wednesdays 6:30pm Pacific and Sundays 2pm Pacific βΒ to play with new technology, explore ideas, work on open source, and hack.
For you it's a chance to hear about new libraries and technologies, peek behind the curtain, ask any and all questions you'd like, and with luck see cool things get built.
Show notes work like a short recap.
PS: you can read and share this online
When websites embed 3rd party content like YouTube, Twitter, Facebook, et all, those embeds come with a bunch of cruft. Shitload of JavaScript, heavy code in iframes, and tracking scripts.
You don't want that.
Privacy is important and so is performance. A blank page with a single YouTube embed makes 18 requests and loads 2.2 megs of data.
5 cookies π³
What if instead of embedding with an iframe, you showed a thumbnail first? Load dynamic content when user interacts and shows intent. Says "Yes, I wanna see this"
The way TechLetter.App works for emails. But without taking you to a new page on click.
Here's the plan:
- take existing AWS Lambda screenshot machinery
- make a Gatsby plugin that turns embeds into thumbnails
- inject vanilla JavaScript to turn into proper embeds
Started with a dirty proof of concept. It worked π€
Time to build a plugin! That did not go well.
First, we ran into issues with plugin building for Gatsby. You have to keep clearing cache, rebuilding the whole site, and waiting a lot.
When your computer is busy streaming, you're waiting a lot.
Should've written tests instead π
But fear not, we got the thumbnail part working. First for YouTube, later we can expand. It's a remark plugin like this:
export default async function (
{ cache, markdownAST },
pluginOptions: PluginOptions
) {
const transformations = [];
visit(markdownAST, "link", (linkNode) => {
const url = linkNode.url as string;
if (url.match(/^http(s)?:\/\/(www\.)?(youtube\.com|youtu\.be)/)) {
transformations.push(async () => embed(linkNode, "youtube"));
}
});
await Promise.all(transformations.map((t) => t()));
return markdownAST;
}
Visits all link
nodes in your document, checks if they're YouTube, and turns them into a thumbnail embed
.
Transformation to an embed looks like this:
async function embed(linkNode, embedType: "youtube") {
const originalUrl = linkNode.url;
const thumbnailUrl = await getThumbnail(originalUrl);
if (thumbnailUrl) {
linkNode.type = "image";
linkNode.url = thumbnailUrl;
linkNode.title = "A youtube video";
linkNode.alt = "A youtube video";
linkNode.children = null;
linkNode.data = {
...linkNode.data,
};
linkNode.data.hProperties = {
...linkNode.data.hProperties,
"data-embed-url": getYouTubeIFrameSrc(originalUrl),
"data-embed-type": embedType,
};
}
return linkNode;
}
linkNode.data.hProperties
is how you can add HTML properties to your Markdown nodes. Didn't know that before, stole it from another plugin on stream βοΈ
The getThumbnail
method talks to my AWS Lambda running Chrome Puppeteer. Takes a screenshot of the YouTube video, saves it to S3, and returns the URL.
You can read about that in Serverless Handbook: Chrome puppeteer chapter.
We're using data-embed-url
and data-embed-type
to tell the injected JavaScript what to do. Meant to turn this thumbnail into an iframe when you click.
Gatsby lets us inject JavaScript on page load. Export an onRouteUpdate
method from gatsby-browser.js
.
exports.onRouteUpdate = () => {
if (document.querySelector("[data-embed-url]")) {
addJS(`
for (let embed of document.querySelectorAll('[data-embed-url]')) {
embed.addEventListener('click', (event) => {
const embed = event.currentTarget;
const type = embed.dataset.embedType;
const url = embed.dataset.embedUrl;
const dimensions = embed.getBoundingClientRect();
if (type === 'youtube') {
const el = document.createElement('p');
el.innerHTML = '<iframe title="Youtube embed" width="'+dimensions.width+'px" height="'+dimensions.height+'px" src="'+url+'" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>';
embed.replaceWith(el);
}
})
}
`);
}
};
Yeah writing code in a string sucks. You don't have React either. And I gotta say vanilla JavaScript has improved lots in the past few years.
This code looks for all elements with a data-embed-url
property and adds a click
listener. When you click, it replaces the element with an HTML string.
Adding it to the page is a matter of injecting a new <script>
tag into document head.
function addJS(jsCode) {
const s = document.createElement(`script`);
s.type = `text/javascript`;
s.innerText = jsCode;
document.getElementsByTagName(`head`)[0].appendChild(s);
}
Where I'm stuck now is with Gatsby shadowing and moving all this code to the existing gatsby-remark-embedder plugin. Got the thumbnails to work, but not the in-browser JavaScript.
Gatsby shadowing says you can put gatsby-browser.js
in your plugin and Gatsby will run that code. But it's not working for me π€¨
Next time!
Cheers, ~Swizec
Continue reading about CodeWithSwiz: Privacy-focused embeds for YouTube, Twitter, et al
Semantically similar articles hand-picked by GPT-4
- Twitter embeds without JavaScript, pt1 β #CodeWithSwiz 29
- Using YouTube as a data source in Gatsbyjs
- Building a small CMS with NextJS, pt2 β CodeWithSwiz
- Livecoding Recap 48- Why contributing to big opensource projects is still hard
- Build privacy-focused blazing fast tweet embeds β CodeWithSwiz 30
Learned something new?
Read more Software Engineering Lessons from Production
I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.
Subscribe below π
Software Engineering Lessons from Production
Join Swizec's Newsletter and get insightful emails π on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.
"Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. π"
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help π swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers π ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are β€οΈ