Onchain SVG Smart Contracts
Listen Now
About This Episode
In this episode of DevNTell, Narb welcomes Nicholas to discuss onchain SVG smart contracts he has built for Juicebox. Nicholas provides an overview of Juicebox, an onchain treasury protocol, and explains how Juicebox projects are represented by NFTs. He demonstrates how he developed a token URI resolver to provide onchain metadata for Juicebox projects, using SVG and base64-encoded fonts. Nicholas walks through the design process, including mockups and the final SVG implementation, and dives into the smart contract code, highlighting functions for data retrieval and layout padding. The session concludes with Nicholas sharing resources for those interested in exploring onchain SVGs.
Key Takeaways
Juicebox is an onchain treasury protocol where projects are represented by NFTs, allowing for composable project management.
Onchain SVGs can provide real-time, dynamic metadata for NFTs directly from the blockchain, eliminating dependence on external servers.
Custom token URI resolvers can be used to generate and update metadata for a whole protocol or individual projects within it.
Implementing onchain SVGs involves challenges such as layout padding for variable data and ensuring cross-browser rendering compatibility.
Base64 encoding allows for embedding complex assets like fonts directly into onchain SVG contracts.
Episode Transcript
Read full transcriptHide transcript
All right, we're live. GM, GA, GE everybody. Welcome to what's going to be another great DevNTell.
So if you didn't know, DevNTell is a 30-minute window for builders to showcase something they're passionate about or have been working on in web3.
This could be an awesome project you've been working on, demonstrating unit testing best practices, automation goodies, smart contracts, how to structure a project, etc. Basically if you've got a passion for something, this is your opportunity to share it with the community here.
And today I am ecstatic to introduce to you Nicholas, who will be giving us a overview of SVG onchain smart contracts that he's built for Juicebox. And Nicholas, welcome. Maybe you want to start off with a brief introduction about yourself and yeah, can take it from there.
Thanks. Thank you for having me, Narb. Yeah, it's great to be here and to hang out with some DevNTell folks.
Yeah, if you don't know me, my name is Nicholas. I'm on Twitter with the name Nicholas with four leading n's, where I talk all about crypto stuff, web3 stuff, usually in Ethereum focus but EVM in general.
Spend a lot of time thinking about NFTs and DAOs and the mixture of the two. I host a podcast on Twitter Spaces every Friday called Web3 Galaxy Brain, which you can check out at web3galaxybrain.com.
And about six weeks ago I started doing a second show on Wednesday afternoons with my friend Jango, who is also a contributor at Juicebox DAO, where we talk about NFT tokenomics and have a kind of office hours workshop session for people who are coming through Juicebox Discord or just generally in the DAO and NFT scene who want to chat about the tokenomics for their next drop or their next DAO project that they're working on. And that's actually been very interesting. We've had some great guests, Jinn from OpenVoxel, Dr. Slurp, who's a successful onchain NFT artist, and his partner Cookie Tree, and lots of other people come through. So that's been a lot of fun too.
So I do some communication stuff around developer relations and onchain dev stuff. And inside of Juicebox I work on governance, operations. Lately I've been doing design stuff related to our governance tooling, a tool called Nance, which maybe I'll show off in the future.
And lately, the latest project that's sort of the first version is being finished up right now is what I'm going to show today, which is this token URI resolver. So maybe I'll switch over to presenting and describe the project a little bit.
Yeah for sure.
So, I thought we could start off by looking at this JD project. So basically, for people who aren't familiar, Juicebox is, well let's jump over very quickly juicebox.money. So Juicebox is an onchain treasury protocol that allows you to in its most simple case collect funds and spend those funds. Most famously ConstitutionDAO, MoonDAO, and AssangeDAO used it in a very basic way. But more deep operational applications of the protocol allow you to essentially state upfront how you're going to use the funds that you collect before you collect them, defining things like how much you have access to, at what frequency, and who you'll be paying, and making all of those parameters explicit onchain so that people can see in advance before they make a contribution how the funds will be used.
So, Juicebox every Juicebox project is represented by an NFT and whoever holds that NFT can control the project, can change what's called the funding cycle configuration, which is to say all the parameters about how funds are collected, spent and tokens issued. So in order to achieve composability the protocol's design is such that whoever has the project NFT, there's one NFT representing each project, whoever holds that NFT can configure the project. This makes it composable with EOA wallets, but also multisigs, etc.
And all of those NFTs live inside of this JBProjects contract. And the JBProject contract is a pretty straightforward contract that has some affordances that make sense for the protocol so that other contracts in the protocol's architecture can talk to it and verify for instance that the correct person owns it in order to decide who can make changes to the project's parameters. But as a consequence of them all being NFTs, they all have NFT metadata which you can see on OpenSea. However, the protocol thus far has not had any metadata applied to it. So all the projects are here but they have no name, they have no image, they have no properties, and even the project overall, the collection overall, has no metadata either. But the protocol is engineered in such a way that we can change that using this setTokenUriResolver function, which allows us to set any contract that conforms to this IJBTokenUriResolver interface, which has just one function, a getUri function and returns the token URI. So any contract that conforms to this can be set as the token URI resolver for the whole protocol. So every project here will eventually get a metadata if that token URI resolver is set. So that sort of explains the context for what we're doing here. The goal of this project was to create a contract that could be set as the token URI resolver to give all of the Juicebox projects their own metadata.
I decided to take on a design that would surface some of the protocol's so if you see here like let's say Juicebox, every project has a treasury which has a balance, it has a volume that it has raised total and a bunch of other parameters, excuse me, a bunch of other parameters like the total number of tokens that have been issued relative to that project, etc. and we can go into the details another day. So what I wanted to do was to create a, actually maybe I could even pull it up on Figma. I wanted to create an onchain token URI resolver that would surface data about the protocol live. Let me see, it's way back in my history where is it? Well, if I can't find it no big deal. I was going to show you the earliest version of this let's see. Yeah, so here are some of the early versions of it. I was thinking of doing historically we used a monospaced font on the website so I thought it would be cool to do something that looked sort of like those old CRT command line interfaces with that orange aesthetic, something inspired by this but surfacing data about the current real-time status of the project. So this would be an example name of a project, what funding cycle it's in, how many days are left in its funding cycle and some stats about it.
So I went through a few different iterations of this and the idea was that it would be monospaced font so I could decide the number of characters in each row explicitly and then do some onchain string concatenation and clever padding work to come up with the right layout so that it would always look good no matter what project it was being applied to. And ultimately I resolved on something pretty much like this. This was the last version of the mockups that I did. There's some other ideas for future versions here. So, the version that I ultimately came up with was the following. So this image shows the Juicebox project. It's actually if the browser supports it, like OpenSea might not allow this, but the SVG that I'm generating here actually has href tags so the Juicebox title is a link to the Juicebox page, also the project owner is a link to the Etherscan page for that address.
And we see some stats about the live status of the project. So what funding cycle it's in, how many days are left in the funding cycle, the balance of the project or the treasury right now, how much of it is overflow which is a technical detail of Juicebox that we can discuss later in questions if there are any, the distribution limit which is how much can be spent, the total supply of the token relative to this project and of course who owns the project. And in future versions I toyed with this a little bit but I didn't get it fully working in time for this demo, but I'd like this to resolve the ENS name of this address if there is one which in this case there is for this multisig. Anyway, this is the file. And to take a closer look at that file we can look at how it looks in the SVG viewer website. So basically this is an SVG that has a big style tag up front and here I'm importing an this is a base64 encoded font or typeface specifically a font the 500 weight Capsules typeface. Capsules is an onchain NFT collection by a friend of mine Peripherylist, who's also a contributor at Juicebox DAO via another DAO called Peel. And this is his project to create this onchain typography and allow people to mint the fonts uploading these seven were uploading the different weights to the chain to Ethereum mainnet and then there's a collection of 7,000 or so NFTs that can use these fonts in their NFTs. So the consequence of this meant of this collection is that this Capsules typeface is onchain.
So in my SVG, I'm importing the Capsules typeface which is already onchain and base64 encoded and I'm using it in a style tag so that the text in these images and these SVGs is all using a font that is stored onchain. So I don't need Google Fonts API or Adobe Fonts and I don't need to care about the OS that is loading this image it'll always load with the correct font because the font is stored on Ethereum, which is pretty crazy and pretty cool. Subsequent to the style tag we have some I've prettified this but in the actual SVG it's not laid out like this there's no spaces etc. But we have a hand-manicured version of the SVG that I drew originally in Figma which I've manually reduced to as little as possible or close to it. And you can see we have these tags, I'm using the where is it? white-space pre style tag which allows me to do spaced use spaces as padding for generating the text so that it lays out correctly.
And we'll look at it in a second the functions that I've got in my contract that are padding all this stuff. It's a little bit gnarly but it does get the job done. And you can even see here concatenating in the Etherscan link to the address that owns the thing. I've also got some filters which are giving it that like glowy look. I don't know if we can zoom in a little bit but it's got a little bit of a glow to it. Oops I clicked the link. Oh hold on. Give me a second I'm going to have to go grab it again because SVG viewer does that thing. Any questions while I look for this?
How how big are the the fonts that are stored onchain? Because that's pretty sick. Yeah.
Good question. Good question. Look how terrible they've made search in the latest Mac update. How big are the fonts? That's a good question. I don't know offhand. It cost me I uploaded one of them in the Capsules collection he did a very cool thing where there were colors for each of the NFTs and the pure value colors for the seven extremes of the color chart were all uploading the the NFTs were free but you paid the gas to upload the file and his contract was able to hash the uploaded content and compare it to a known value hash of the existing files that he expected you to upload to make sure that you were uploading the right thing. So he got his collectors including me and a few other people here you can see to spend the gas to upload them. So I'm using the 500 weight one so I think oh no I guess that goes to Kenny. I'm not sure if we can jump right to the thing but anyway I'll look later during question period for how much it was but I think it was about .1 it was under 1 ETH. I think it was .1 or .3 ETH something like that to upload each of the font weights because they're all different files.
Gotcha.
So I was looking for this thing. Yeah, here we go. So yeah, we were just finishing up looking at this. So the filters give the color and some of the glowing effect and frankly these are things that I just grabbed from the Figma file I'm not totally sure how they all work. Although I will tell you one debugging thing that I had to struggle with during the process was that this was really not rendering correctly in Safari and the solution ultimately was to not use white-space pre-wrap but instead to switch to pre because Safari does not correctly render all of the CSS tags in SVGs. In SVGs they don't honor every aspect of the CSS style tags so switching to pre solved my problem where they were all jumbled on the left side. So if you're doing onchain SVG that might happen to you. So that's kind of an overview of how the SVG works.
So now we can take a look at the contract. So at first I wrote this this contract which ultimately got renamed to DefaultTokenUriResolver, but this is the contract that does a lot of the heavy lifting. In the end it got shuttled off into a separate contract but at first this was the only contract there was and we can take a quick look at it. So basically I import a lot of different libraries things relative to Juicebox that I need to discover all of the onchain metadata that I'd like to incorporate into the NFTs. Then I have this StringSlicer contract. The reason this is not an internal function to the actual contract is because I need to pass it a string memory and call it to use this array slices function, this little magic here, which can only be done on call data, not on memory like not on a memory string. So I had to put it in a separate contract and that's why it's at the top of this file. Then we've got the main contract where I bring in the slicer tool that I just described and some events that I'm going to use and I declare all of these values that I'm going to make use of later on. I also have this themes thing which we can take a look in a minute but you can keep in mind that there's a mapping of project ID to theme. I do the constructor set up the whole all of the values that I just described above and discover some others that can be discovered onchain by doing lookups. Trying to reduce as possible as many as possible the number of constructor arguments although of course I could just hardcode in all of these values in here. And I think that's one thing that in the future I'd like to tighten up even further to make this discovery of other contracts addresses even more executed onchain at deploy time or even at lookup time for the ones that can change.
I also set a default theme here which just describes some colors. Those are the orange and brown colors in the the image that you saw earlier. This function getFont goes and grabs the base64 encoded typeface that I was looking for and I'm using here the Capsules typeface this is an address of the contract that I know in advance. Then we start to get into some of the more gnarly padding stuff. So these padding functions I use in order to I define if it's left padding or right padding, I give it the string that I'd like to pad and I give it the target length. And I'm using this in order to generate essentially 15 characters on the left side and 15 believe it's 15 if I recall correctly this part I did a little while ago and 15 on the right so that regardless of the length of the value that I get here it slot it adds the correct number of spaces here so that it fits in correctly. And vice versa for these ones I add spaces here so that the I sort of do them half-half. the left side and the right side split down the middle and I also do things like this adding an ellipsis if the value is very long which comes into play especially for things like total supply.
That's how that works. So I won't belabor this because I know we're already running short on time. So essentially there is this padding function which does a lot of nasty stuff to come up with the the appropriate length string it can return. I have these helper functions like getProjectName which rely on another contract called ProjectHandles also by Peripherylist the creator of that typeface which and other collaborators who helped improve the code. And this is what gives Juicebox projects like this name here which is an based on an ENS handle that Juicebox project owners can associate with their project. So I wanted to pull those in and this code goes and grabs them if there is if there is nothing then it just uses the project's number and concatenates in the ID number of the project. But if there is a handle it it uses that handle like you saw there at Juicebox above.
Here are some more helper functions which I won't describe in detail but you get the idea that these are all allowing me to generate the elements of the text that go into the SVG. So get left padded time left padded time left maybe could do a little bit improving on the names there but you can see that these are figuring out depending on the funding cycle duration left it adds in the space hours or space days or minutes. And it does some logic to figure out how to do that including like some intelligent or maybe intelligent somewhat intelligent if else statements to determine if it's like should be plural essentially. FC time left row is that funding cycle number and balance distribution now we're getting into these here. I'll skim through these and jump down to so these are just more helper functions total supply. Theming we'll get to in a minute and now we get to the the real meat of the contract which is this getUri function.
So this is the function that IJB TokenUriResolver the interface that this contract must correspond to has to have this one function and so I first check if there's a theme set and if there isn't we just use the default theme. Then I go and grab all of the relevant like contracts to this particular project because different projects can set different contracts that they interact with in the Juicebox protocol. I go and check if it's got an ERC-20 associated with it. And then I start concatenating all of the values to start creating the SVG. So here we've got the project owner you can see here I'm grabbing their address. Don't yet have the ENS stuff in so that will come in later. And then here I start concatenating the overall metadata blob that you're going to get back when you call that token URI function on the JD projects. So you can see here I'm starting to create the I'm going to base64 encode the whole of the the whole of the metadata JSON. And then here I start declaring the metadata so I've got the name, the project's name and this is the descriptive metadata like textual descriptive metadata that OpenSea will populate its text field for example. I'm also concatenating in things like how much ETH is in the treasury the overflow directly into the description text that'll be in OpenSea and other NFT viewers. Then when we get to the image tag we break off again and start doing the more complicated part which is concatenating in all of the data we got from the chain from like real-time onchain data into the SVG.
So I've broken up I've done this this way I'm sure there are other ways of doing this but I've just taken the SVG that we looked at earlier and essentially cut it off at all the parts where I need for instance the font. So you know font true type whatever its name is Capsules and here's the base64 encoded font and I have that helper function that goes and grabs it from the corresponding contract composably onchain elsewhere. Then we have things like the theming that gets plugged in. So we're using the default theme right now. We have the link for the project so I just used the we have this functionality on the Juicebox.money website. So just drop in the projects ID there. Then we have the project's name so this would be this part up here and that will already have been decided in that helper function whether it is at Juicebox or whatever project we're looking at or the project's ID number if it has no name set. And then I'm going and grabbing from all these different helper functions the balance etc. and I've broken them up with comments here so you can see there's like overflow, we have a text element that's 96 pixels down from the top of the image top of the SVG. and then I'm going to pull in the overflow row which depends on the overflow string the overflow row here and in all of these you can see a similar kind of pattern. The overflow row just introduces the the label and then the overflow string introduces the value and some spaces around it.
That's how those work and you can see we're doing like line by line so maybe it's you could think of this SVG as a kind of programmer mindset about how to construct an image. There are other onchain SVG projects that are you know not so like straightforward in how they're constructing their SVG but this is helpful for me because there's so many different projects that the data sort of normalizing the data across them by doing things like abbreviating them if they have too much too long of a value was a concern of mine. However that could have been avoided by just doing them you know each value on its own row potentially. That's it we finish up the string, got the owner, the owner's name and then I have some other values down here in the filter area for the themes. Excuse me. Finally we finish up the big string the SVG and the JSON ends there and then we concatenate all those different parts the sort of prefix and then the SVGs data which gets base64 encoded after that base64 prefix because we return you know some nested base64 encoded data like for instance the typeface is base64 encoded inside of the SVG which is base64 encoded inside of the JSON which is base64 encoded. So when OpenSea or whomever asks for the token URI they get this base64 encoded blob and they have to decode it and then decode and decode again to get down to the the ultimate SVG that we were looking at a moment ago. And then down here we've just got some helper functions to ASCII string helps to actually there was one thing I wanted to talk about here Unicode I use here it's necessary if you're going to use things like this element and this element or if you were going to use emojis. And these are just some helper functions that allow for string manipulation and switching between different types of values which is useful for creating this thing.
So that's how it works. It has a pretty good readme. It's currently sitting in a branch on this token URI resolver where is it? GitHub? sorry token resolver GitHub in a PR so it'll be in the main in next week or so. But if you want to check it out today you can check it out here at JBX Protocol Juice Token Resolver and then migrating from old repo is the name of the branch. And I have a pretty extensive readme about how you can test it on your own. There is a little change to the protocol in the last week that has caused it to fail because one of the contract addresses has changed since this was last updated. So I'll be changing that probably later today.
But if you want you can run these tests. One other interesting thing about this is that almost all of the testing I'm doing is mainnet forking. So you can and the reason for that is that it relies on the protocol extensively and it's kind of a pain to deploy both the protocol, create projects on the protocol, fund those projects and then also set up redeploy ENS and grab an ENS name and then associate a project handle with a project. It was just too much work to try to figure out to do all that. So I've been doing all this through mainnet testing and if you're interested in doing that one thing I suggest is to obviously this is all done with forge as you can see from from here and this is really the the guts of the command that you need to do to test it. I highly suggest fork block number because forge will cache the the block data so you don't need to redownload it every time you want to test. So that reduces the testing time significantly. I'm also using via IR because there's a lot of data moving around and I'm using FFI which I'll show you in a second how that's how that's being used. I know we're coming up on the time so I one thing I wanted to explain before we finish up is that at about midway through the process of creating this thing, there was an architectural change to instead of just hardcoding this contract as the token URI resolver, I switched based on a conversation with Peripherylist to having the this become the default token URI resolver that is a dependency of the token URI resolver which is really just a much simpler contract and a registry contract.
So in this token URI resolver and this is ultimately what will be set on that JB project as the token URI resolver. I have a mapping of uints or project IDs to resolvers. If the projects have no custom resolver set then it uses the default token URI resolver which is what we looked at earlier. However this lets us both update the default resolver if I want to change something about how that SVG is created I can deploy a new one and just associate it as the default on this contract here. Where's the default one? Somewhere here. And every project that doesn't have a custom one set will be updated with that new default resolver. But at the same time anybody can any project can come in and create their own onchain or offchain resolver, deploy a contract it could rely on IPFS or something else and they can use this contract to set their token resolver for their project. It does a little check to make sure that they have permission to do so using a functionality provided by the Juicebox protocol to check if they are either the owner or someone who has been authorized by the owner to change their token URI resolver and if so they just pass the contract that they want to use instead and when anybody OpenSea or whomever checks the token URI on their project, this contract will return their resolver rather than the default one. So and as I said this also allows us to update the default one if we discover a bug or want to make an improvement. So in the end this has become the token URI resolver this much simpler contract like 100 lines of code and this much more complicated one which almost certainly has bugs and things that we'd like to improve becomes something that is more modular and can be changed over time. So that's the ultimate structure of this contract. I have some templates we could talk about if there's time. but I guess are there any questions?
Yeah gang, if you have any questions please post them in the chat and I will bring them up on screen. Um and while we wait for that, I guess one question I had um is how does the gas look on this? Um you see it's quite a bit of code um to get all the elements and slice yeah.
Yeah um I haven't checked in a little while. I think it's like .15 ETH to upload everything, like to deploy both of these contracts. So honestly not that bad um considering how much work it's doing and I'm sure there's optimizations that could be made here um but it's not you know it's not 10 ETH or some like I know um in the initial version of um what's it called? CyberBrokers, they put like 91 ETH worth of SVGs onchain in the first version. It's not it's not that bad.
Yeah. Yeah, pales in comparison. And you mentioned the SVGs have hrefs on them. Uh we haven't seen that, at least I haven't seen that used as much as it could be. Like what's your opinion on those and whether there's like
So I actually I have to the one thing I haven't done here is put this on testnet yet um because things have been moving around a little bit with the protocol. I'm curious to see if OpenSea resolves them. I know that OpenSea I don't have a good example on hand but you can actually like in the metadata you can render a whole browser and even click links and it will navigate to those links within the the image here. So I think I think it's going to work on OpenSea. I think it's a cool idea. I think ultimately someone's going to do something bad with it and the sandbox will be constrained even more. But until that happens uh you know let's have fun.
I remember trying it um last year and it wouldn't uh render the links on OpenSea itself but if you actually yeah, but maybe something's changed now, I don't know.
I have seen them but it was on the testnet one so maybe they're more lenient on testnet I'm not sure.
Possibly. Possibly.
I guess I could show one last thing. So I added a little for fun I added this theming thing to the default contract. So let's see, oh jeez that's going to look terrible but I'll do it anyway. let's see. Oh gosh I have to remember what the command is. So I have a bunch of different tests and I created this theming thing so for people who don't want to deploy their whole own metadata contract they can just add a theme they can set a theme for their project on this default resolver that I've created and that just lets them change the color scheme of their metadata. Let me see if I can show you how it works.
It's going to take a second to work. And one thing I did mention is this FFI thing. Won't have time to go into it really but basically a friend uh this guy Jeton Connu, French dev from Quebec is created this little FFI script that I'm using. So basically it opens the result it saves it uses node to save the resulting file to the file system and then opens it in your default SVG renderer which for me is Brave. So you can see here with the theming I've set some horrific colors of course but project could change their color scheme to whatever they like so that they can have a little bit of customization by just sending a little easy transaction with three values using Etherscan or Etherfunk or whatever tool they prefer.
Awesome. And uh I guess since we're kind of coming up on time here, what's the best way for people to um follow up with you if they have questions or want to learn more about developing SVGs onchain?
Yeah definitely hit me up on Twitter is where I'm most responsive. That's probably the best place just DM me my DMs are open. Otherwise come hang out if you're curious about the Juicebox part of this come hang out in the Discord it's discord.gg/juicebox.
We didn't really get time to talk about it but I do have I'll just show quickly if you're interested in this kind of thing for yourself, there are lots of tools out there and these are not like the best tools but in terms of speed to get you up and running you can download actually sorry this one will give you Juice SVG Template in the JBX Protocol will give you a lot of the things that I talked about pre-imported. Like it gives you the Capsules typeface and it gives you the project Juicebox project handles already in the thing on different branches here. And this is a quick way to get started. And if you just want to get started with SVGs onchain in the first place, let me just find the link. I did an even more stripped down version of this that really just like within 30 seconds you just git clone this thing, as everybody listening probably knows, git clone that, run forge install and yarn install and then call this and you'll immediately, actually I can show you right now if you just do that it gives you an SVG rendered in your browser right away. So you can immediately like jump in and start changing things. change this to hi and remove that and call it again and it's done. So if you're interested in onchain SVGs but have been intimidated by it, I think this is a really nice place to start. Once you've mastered this you can also check out Winter's Hot Chain SVG which is another great tool for drawing SVGs in your favorite code editor.
Awesome. Thank you so much Nicholas. Really appreciate you coming on. Yeah, this was really awesome to see and hopefully if you were scared before by onchain SVGs, you now have a path forward.
Yeah. Thank you all for coming to check this out and thank you for hosting me.
Yeah, my pleasure. And just before we leave off, I want to leave our audience with a little info on our operators in the DAO. Did you know that Developer DAO's fundraising operators Chuck and Wickist are here to help you fundraise for your projects? In their fundraising hub on Discord, you can get support in writing grant applications, learning about fundraising, budgeting, pitching, and they even help you introduce to grant leads from different protocols. So if you're interested, be sure to drop by their office hours, that's held twice weekly or schedule a one-on-one with them. And with that, I want to wish everybody a very happy Friday, happy weekend, happy ETH Denver. ETH Denver kicks off today. Hopefully you're all soaking it in and not freezing it up in the -20 Celsius temperature. Hope you all brought coats. But yeah I will be dropping a link for people to claim the POAPs in the comments here as well as in the Developer DAO Discord. And with that, we will see you here next week. All the best gang.
Thank you. Cheers. Bye.
See ya.
Listen On
Share This Episode
Share on XWatch Episodes Live!
Subscribe to our event calendar and never miss a live episode.
View Event Calendar