Learn to Fly with Laravel Reverb
Joe takes us on a journey through real-time communication with Laravel Reverb
Transcript
00:00:00 good afternoon LaRon how are you all doing awesome thank you for that really warm welcome I really appreciate it made me feel a little bit more settled here on the stage um today we're going to learn how to fly with laravel Reverb and we're going to split down this talk into two parts the first part is going to be quite Theory heavy so I hope you're all uh suitably caffeinated but we're going to redress the balance in the second half and we're going to jump into a really fun demonstration but before we get to too
00:00:30 far into it there's a little bit of housekeeping that we need to do uh in case you couldn't tell from my walk-on music or the accent that I'm speaking to you with now I am indeed from the UK and that means there are a couple of subtle differences between the flavor of English that I speak and the flavor of English that most of you in the crowd speak so we're just going to align on a few terms before we get started so that you don't get confused later on in the talk okay so apologies in advance for
00:00:58 this one but being from the UK we say sorry all the time it's just what we do if we're walking around the ventilator and you bump into me I'm going to apologize to you that's totally normal don't don't worry about it and first off I'm sorry for that yeah sorry we jumped on too um okay so when I huh oh sorry yeah again okay so when I say the word football you're probably all more familiar with the term soccer and when I say root what I really mean is Route and when I say beta I mean beta and finally
00:01:36 when I say Ticky boo that means all things are great okay so now we have that sorted let's talk a little bit about me I am Joe Dixon and I uh have the absolute honor and privilege of not working with just these guys but the entire incredibly talented awesome laral team am I role as engineering team lead on the cloud project now fun fact about this photo Kristoff took this yesterday while Taylor was just announcing Cloud to the world and this is the whole team making sure that everything ran smoothly um I live with my wife and two
00:02:17 kids um and I feel a little bit guilty right now because I left for Dallas on Sunday afternoon and I have yet to call them I've been so busy since I've been here but thankfully assuming the stream is working then are watching so I just want to call out to them and let them know I am okay um and I will check in and call you right after I get off stage we live in a small City in the UK called Norwich where we're lucky enough to have two Cathedrals one castle and the best football team in the world now quick show of hands does
00:02:52 anybody recognize this guy on the screen all right we got three or four football fans in the room that's good yeah this is in fact n's best player when he's not injured uh and US national team attacking player Josh Sergeant so yeah when I'm not at my keyboard I'm away from my keyboard I'm chilling I'm hanging with my family or I'm watching football or I'm watching Formula 1 okay that was a CH GPT creation um and if I'm not doing those things I'm probably drinking coffee I'm a bit of a coffee nerd so if any of you want to
00:03:28 take switch gears from Talking Tech during the conference find me walking around I'm happy to talk with you at length about any of these topics okay enough about me let's jump into the meat of this talk we're going to be talking about laravel Reverb so to start what is laral Reverb well in short Reverb is a first-party package from the uh from laral it provides realtime websocket communication to our laral applications it's blazing fast incredibly efficient and built out of the box to scale we announced rer back at LaRon Au
00:04:06 uh EU not Au in February of this year before releasing the first beta version on March the 12th in line with the release of laral 11 and after a successful beta period we tagged V1 on July the 2nd and the nice thing about us being able to line up the release of Reverb with the release of larval 11 was that we could lean into some of the uh uh slimming of the skeleton that was um that made it into that release and in particular we can hook into the install broadcasting command so if you want to enable broadcasting in your laral
00:04:40 application you now have to run this PHP Artisan install broadcasting command and because that command now exists we're able to make the install process of Reverb incredibly easy in fact when you run that command you're immediately prompted if you want to install Reverb and if you do it's going to go ahead and install Reverb into your composer dependencies and update them there and then during the command and in a similar fashion it will do the same with laravel Echo the frontend companion for laravel's
00:05:10 broadcasting capabilities it will add that if you want to install it it will add it to your node dependencies update your node dependencies and recompile your front-end assets and finally we set a really sensible set of default configuration options so all that's left to do after that command has run is to start your Reverb server with PHP partis Reverb start and at this point you have a realtime websocket connection between the back end and the front end of your application and you're able to send
00:05:40 events between the two in real time via websockets my slides are not moving thank you for the Applause then uh so to understand how R can benefit our applications we probably need to take a little bit of a deeper look into into what websockets actually are and to do that let's look at a a UI component that we've probably all built a million times over something like a status indicator now a status indicator is something that we want on the front end of our application which reflects the state of our backend application so
00:06:15 if we're processing jobs or web hooks or running commands in the back end of our application which are updating State we want those changes reflected on the front end of our application in real time and there's a couple of different ways that we can approach that probably the approach that we're most used to taking is something called short polling where the client will kick off a request to the server to ask for an update of the status and the server will send the response back and the client will update the UI
00:06:42 accordingly and then we wait for however long is uh we feel is right before kicking off that process again and again and depending on how long that wait time is in the middle we can start to build up something which feels real time of course it's not real time we have these weights in the middle and we make multiple expensive HTTP requests every time we do this websockets work a little bit differently so we still have our client and our server our client makes a request to the server and then we go through a slightly
00:07:14 different handshake negotiation to your standard HTTP handshake negotiation but assuming that negotiation completes successfully we end up with this open pipe this web socket between the client and server which the the client and the server are free to send data between each other as and when they needed and this connection will remain open for as long as we need it to be and so we're not making multiple HTTP requests we're making one HTTP request which gets upgraded to a websocket uh connection
00:07:44 and then that's a longli connection which we can use as and when we need but how does Reverb work because typically when we're building PHP applications we are used to taking a request processing it and sending a response back to the client closing it down and then kicking off the next one so for RAB we had to think a little bit differently and after trialing multiple approaches we landed on using a lowlevel library called react PHP and react PHP takes an event driven Paradigm and it's incredibly efficient
00:08:19 it allows us to uh act in a non-blocking asynchronous fashion essentially what it allows us to do is take a connection allow it to sit in memory and only act on that connection as and when new data is available and I want to talk to you a little bit more about how that works now okay so full disclosure this is where the next three slides are quite Theory heavy so if you can if you can if I can keep your attention I'd be really thankful and then we'll redress the balance and we'll have a whole lot of
00:08:46 fun in the second part so with r PHP it all starts with an event Loop um and this is the lowest level react PHP component which we have available to us and it's what powers everything that react pH all of the components that react PHP has on top of that and what you'll be interested might be interested to know is it's nothing really any more than a while true loop it's an infinite Loop but three things happen on each iteration or tick of our Loop the first thing we do is process future ticks now this might sound a
00:09:20 little bit counterintuitive but what future ticks are are things which have happened on uh a previous iteration of the loop which we've pushed to a future tick of the loop so in our current iteration we're processing something which we deferred from a previous Loop tick and that's really useful for doing things like uh chunking up huge jobs into smaller ones because if we do huge Compu intensive jobs on each iteration of the loop we start to block other events from being able to be processed this is all single-threaded so it can be
00:09:49 incredibly efficient but we have to figure out and we have to work with it to understand what the most efficient way of doing things is okay so the next thing that we process is timers now these are things which we may want to schedule on a one-off basis in the future or things that we want to schedule on a periodic basis so every 1 second every 5 Seconds every minute or every 10 minutes and re actually leverages timers quite heavily for doing things like pruning stale connections um and actually ingesting
00:10:18 events into our first party integration with laravel pul which is how we monitor the performance of our Reverb server and finally and perhaps the most uh powerful part of the event Loop is if we process input output and streams I'm going to talk about this a little bit more in in the next couple of slides but this is where things get really exciting and we can do some really cool things so once input output and streams have been processed the loop tick completes and it starts again and so every event that's been queed up will
00:10:49 get processed in the in the every event that gets queed up from the previous iteration of the loop will get processed on the subsequent iteration of the loop it turns out this is incredibly efficient but how do we use this in Reverb well we still have our event loop we're going to deal with some input output and streams because at the uh at the base of it we're dealing with HTTP connections and to do that we're going to leverage another component of react PHP called the socket component which allows us to start a TCP server on our
00:11:20 event Loop and what that does is the TCP server is started on a host name and port number and it just listens for new connction connections so in the in the case of R let's walk that through we have a client who wishes to connect to our websocket server you can see okay you can see on the bottom right hand side of the screen that we're making an HTTP connection here it's a standard HTTP get request but there's a couple of additional headers which we may not be so familiar with seeing and these are the things which make
00:11:54 websockets possible we're asking the server to upgrade the connection to a websocket connection and we are sending through some other headers which are used in the handshake negotiation process so rev is sitting listening for that connection and when it receives it on the next iteration of the loop we process it and then reub understands that this is a websocket connection and we go through the handshake negotiation process and assuming that all works out correctly we end up with it user stored
00:12:24 in memory but of course we need to notify that use of that connection has been established so we queue up an event on our Loop we now have an open stream between our Cent client and our server we can push data between the two but remember this is event driven so the next time our Loop ticks that event which has been queed up will be processed and we write that event back to our subscribed client or a connected client and this can happen multiple times in this case three but Reverb is efficient enough that it can handle tens
00:12:54 of thousands of concurrent connections on a single PHP process so now our client wishes to subscribe to our status Channel this is the channel which we will broadcast to from the back end of our laral application to notify the UI that changes have been made so they send that AC request across our websocket that event will get queued up the next time the loop ticks rer picks up that message understands that it's from user one and makes a reference that user one wishes to connect to the status Channel
00:13:28 and the status channel is nothing more than in a PHP array which stores references to subscribed clients and of course we want to do something similar as when our connection was successful we want to let the user know that their subscription was successful so we queue up that event and the next time the loop ticks we write that back to our connected client okay so we have three users all connected to our websocket server one of those users is subscribed to the status Channel and now our laral application
00:13:55 has detected some State change in the back end of our application and wants to notify those connected users or the the one connected user in this case that we have subscribed to our status Channel and that works a little bit differently here we're making a standard HTTP post request to the SL Event Route and this route isn't registered in our laral application it's all handled by Reverb uh and the roots that it registers internally and here you can see we're sending the payload which we want to
00:14:22 broadcast to those connected or those subscribed clients so again that event or that connection gets queued up the next time our Loop ticks we process it Reverb understands that this is a request to broadcast that message it's going to go ahead and look up the users subscribe to the status Channel and it's going to forward that event across to them and so in this case the next time our Loop ticks we're going to do two things we're going to send our event onto our connected client and we're going to send a 200 okay back to our
00:14:52 laral application to let laral know that that event was broadcasted successfully so the next time the loop ticks both of those events approach processed and both of our connected uh or sorry our websocket connection and our web server connection are notified and if you notice the request that we had in memory has now disappeared because this is a standard HTTP request once we've sent that 200 okay we don't need to deal with it anymore that's all completed but our websocket connection to user one remains
00:15:19 established and that user remains subscribed to the status channel so obviously there's a lot more going on under the hood in Reverb but in Broad strokes this is how it works and this is how we're able to build something that's so incredibly efficient in PHP now your mileage will vary depending on exactly what you're doing but I've already mentioned that Reverb can handle single Reverb server single PHP process can handle tens of thousands of concurrent requests in fact we already have uh laravel Forge and envo running on a
00:15:53 single server it's not a big server we have both of them running on a single server because RB is multi-talented out the box and that server isn't even breaking a sweat and we're processing millions of requests every day but if you do end up in the situation where you are processing hundreds of millions of requests a day or tens of millions of requests a day then RAB is built out of the box to scale horizontally and this was quite an interesting problem to solve so I want to just talk a little bit more about how
00:16:22 we did that so here you can see that we have our infrastructure set up we have our our load balancer and our load sat behind our load balancer we have three servers each running Reverb so we have a separate event Loop for each one of those things none none of those servers are connected they don't know about each other but at the bottom you can see that we also have a reddis instance and this is where things get a little bit more interesting so each of those Reverb servers are subscribed to a pubsub channel on that
00:16:52 redis instance and that is all handled by the event loop as well so if there's any events which are broadcast or published to that uh reddis Pub sub Channel then those events will get processed on the next iteration or the next tick of the loop as we just saw with Reverb itself and that's going to be important so we're going to look at what happens now when users start to connect so our first user hits the load balancer the load balancer sends that request to server one and that's now managing that connection and its
00:17:23 subscription to the status Channel now the second user connects that hits the load balancer the load balance affords the request to server two and the third user connects hits the low balancer server three so we now have three users all handled managed by different servers but all connected to the same channel now hopefully it's already obvious what the problem is that we have to solve but let's throw our laral application in the mix so our laral application makes that post request to the SL events
00:17:54 route and it hits the load balancer and the load balancer sends that request to server one so ordinarily what would happen here is that our rever uh server would find all of the connections subscribed to the status Channel and broadcast it to those connections but because that post request has only been received by one server only the the user or the connection managed by that server would receive that event so users two and three wouldn't receive it so what happens instead is server one who's received the event who has re
00:18:26 which has received the event publishes that event to our reddis Pub sub Channel and because we have those three servers listening on the same channel the next time I'll Loop ticks each of those servers will receive the event and each of those servers can then forward or broadcast the request onto the connections that they manage so with this implementation of Reverb you can really scale things as far as you need to go and all things are Ticky boo all right let's have the fun part thank you just going to have to give me a
00:19:14 moment to set up okay I'm not going to connect to Ben those lightning bolts are scary okay we're connected okay so what we have is uh this I'm going to bump the sides up a bit uh this incredibly beautiful Nintendo switch UI which I have lovingly crafted with nothing but Alpine JS and Tailwind CSS yeah I know right who knew not all tail insights look the same okay so so what we're going to do is uh build on this UI and make it interactive using websockets so I'm also going to jump into my editor and I wish this was a bit
00:20:10 bigger out the gate can everybody see that okay and we're going to run not that this Reverb fly command now what you've probably noticed is that this isn't the Reverb start command and that's because we're going to be doing some things with the event Loop which Powers Reverb and so we need access to that we're going to hand roll our own command but let's jump back into our browser let's quickly open the network Tab and let's make sure we have a connection to our websocket server which you can see that we do so we
00:20:40 subscribed successfully and we are subscribed to a channel a private Channel called fly right let's see what this UI can do all right I bet you didn't see that coming okay so we can fly control this drone from the UI I can fly up I'm going to fly too high because if I take down one of those cubes Taylor's going to kill me and I can fly it down I can fly left I can fly right I can fly forward but not too far because I had to get special permission and they told me I'm not allowed to fly this over the audience I
00:21:20 can fly back I can even flip all right cool okay so that's pretty cool right we're using websockets to communicate with this drone using this UI but what else can we do what happens if I press this button all right now we're getting Telemetry from the Drone so I can now fly left and right and you can see the speed that I'm flying at I can fly up and down and you can see at the bottom left my the altitude the Drone is flying at and the bottom right you can see the temperature of the Drone and the top
00:21:57 left you can see the battery St so that's pretty cool and I've been told I can't fly this thing for too long so at the risk getting kicked out before I get to finish the talk oh no we don't want to land I didn't press any buttons for long enough let's take off again here we go I'm going to press this button and see what happens in fact I'm going to fly up a little bit higher first oh no ah all right let's see what happens we [Applause] flip okay one more thing to show I just want to show the performance of the
00:22:47 event Loop so I'm going to have to refresh the page I think you can see all of the events that we have flying through via websockets all of the streaming events I just had to press that before it landed again all of the streaming events are just flying through here and all of the all of the video streaming events all of the events that we are receiving from the Drone and I don't know if we'll be able to see it can't quite see it because these things are coming in too fast but you'll be able to see the
00:23:14 interaction that I'm sending to the Drone as well we're going to come on to that a little bit more in a second but as a little bit of fun who thinks I can land this thing back in the Box all right this is hard give me some help am I right left stage right I don't know what that [Applause] means I can't tell you how scared I was about doing that okay so let's have a look at what's actually happening here so the first thing we're doing in the handle command the handle method of our Command is we are instantiating a
00:24:06 new event Loop what we have to do is Loop colon colon get and we get a new event Loop and the first thing we're going to do is create our websocket server and this is pretty much exactly what happens in the Reverb start command we're just using the server Factory and in this case passing through our event Loop and our configuration options and creating our new websocket server which will run on our event Loop so that's all Reverb that's nothing to do with the custom I'm doing in addition then you can see we have these
00:24:34 three methods FL uh fly status and display they all receive the same event Loop fly is the method which controls the Drone status is the method which allows us to read the Telemetry from the Drone and display is the method which allows us to uh consume the video feed so we're going to jump through these one by one okay so the fly method not too much happening here but we've got a new class which we're not familiar with this datagram Factory and this is another component which ships with react PHP and
00:25:04 it allows us to communicate over UDP Universal datagram protocol and that as it happens is what we need to communicate with our drone so when we instantiate or when we call this fly method and pass through our event loop we're creating a client because we want to be able to send messages to the server running the UDP server running on the Drone when that connection is made we receive our instance of our socket that's the thing that we can use to interact with the drone over UDP and I'm binding a Singleton to The Container
00:25:35 this fly service and fly service receives our socket so let's jump through and have a look at what happens in here so in the Constructor we're immediately sending the command statement to our drone and this is the thing that tells it we want to be able to control it it's just what we have to do to put it in the right mode so that's going to happen as soon as this class is instantiated and the only other thing that's interesting here is we have this send method which pretty much does the same thing we send a command to the
00:26:02 Drone but where are those commands coming from we need a string it's just a short string that we pass to the Drone to tell it what to do how are we getting that from clicking the button in the UI into this service to communicate with the Drone and this isn't something I would necessarily recommend that you do but I'm actually cheat teaching cheating the system a little bit so RB ships with this feature called client Whispers And client whispers are designed to or designed for clients to be able to Comm
00:26:31 to communicate with each other without having to interact with your laral application at all so what happens is when you send a client whisper that whisper is sent to reverb and Reverb then immediately just broadcasts that to all the other connected clients on that server but Reverb also fires off a message received event on every message which it receives so every time it receives this client whisper it finds an event and because it's an event we can create our own listener so in this handle command listener we're listening for our
00:27:03 message received event this is where things can get a little dangerous because I mentioned before that if you do large amounts of compute work large amounts of processing on each tick of the loop then you're going to slow other things down from being processed you have to use this carefully I would not recommend interacting with your database or making HTTP requests or anything like that and worse than that you can actually end up in a deadlock so if you perform some action which uh relies on the next iteration of the loop then
00:27:29 those two things can never complete and so our Loop gets blocked completely that's definitely not something that we want but we're being really thoughtful here and we're not doing too much work we're basically interrogating the message on the event and we are checking to see whether the string starts with client hyphen which is what all client Whispers start with if it's not we're bailing immediately otherwise we are taking everything after the client hyphen and that becomes our Command and then all we
00:27:54 do is resolve our fly service from the container and we pipe that uh that command straight through using our send method meth send method to the Drone all right so that's how fly Works let's jump back into our Command and we're going to look at status okay our old friend the datagram factory is back but this time again using the same event loop we're doing we're creating a server because we want the Drone to be able to send stuff to us so the Drone communicates or knows to past data over UDP to this IP address
00:28:30 and Port that we have configured here when that con connection is made we are listening for new messages on that server and then we're just delegating everything to reverb Reverb ships with this event dispatcher and we dispatch the event directly with an event name called State changed on our private fly Channel which I showed you we were connected to at the very beginning and this past State method is just what I use because the Drone gives you a really gnarly string to give you all of the Telemetry data so
00:28:55 I'm just passing that out turning into a PHP array and making that useful when we broadcast it to the front end so when the front end receives that event it can cherry pick the data that it wants to render on the front end of our UI okay now for the big one this was a fun one I almost didn't get this working but I'm glad I did okay so the display method again receives our event Loop and we're using another component of react PHP here we're using the child process component and what we're doing is firing
00:29:23 off a separate command but using again our event Loop and this command uses or we're just firing off an ffmpeg command which again listens on UDP using the IP address and the port number of the server and what we're doing and cheating a little bit here what you saw on the screen wasn't a true video feed what I'm doing is taking each frame of the video converting that to a JPEG image and piping that straight out to standard out so again a little bit further back up we kick off our process here with the
00:29:55 start method and we pass through our event Loop and then we're able to start listening for events on standard app so every time we receive a new data event on standard app that's going to be our new image from ffmp and again we're just firing this off to reverb so reverbs listening uh sorry we're listening for that event and reverbs we're then using reverb's event dispatcher to fire off an event called video stream on the same private fly Channel which we've talked about before and all we're doing is base 64
00:30:21 encoding that image chunk and firing that straight off to the front end when the front end then receives that event we are just taking that data decoding it and squirting it straight into a canvas tag that screen in the middle of the UI is nothing more than a canvas tag and we're just updating the image on a regular enough basis that it feels like a video stream it's pretty cool stuff and it shows the power of the event Loop we've got loads of things happening here we've got websockets HTTP connections
00:30:47 being handled by our event Loop and Reverb and then we're communicating over multiple different types of UDP connection using the same event Loop and we're receiving tons of data from this drone and pushing more data to that drone all on one PHP process which I think it's pretty cool okay thank you I appreciate that it was a lot of fun oh sorry Aron too soon that was a lot of fun to build it was fun to build something that was a little bit different to your standard kind of chat app but I think hopefully it's giv
00:31:34 you some inspiration of the kinds of things that you can do with Reverb and the event Loop that powers it so I'd love to hear the kinds of things that you're building with Reverb if it's standard stuff great I'd love to kind of hear about how it's working the performance you're getting from it any problems you have that you want you think we could maybe address and if you're doing quirky things with it even better I love to hear about that stuff too uh I've been Joe Dixon you can find me _ Joe Dixon I'm only on Twitter
00:31:57 that's the only place I hang out and I will go there until the sink ships thank you very much hey everyone my bad huh sorry about that uh one those cool yeah but you see you came in up here and I wasn't ready for it so that was the whole thing um all right a couple a couple of questions primarily we're going to start with this when when did this enter your mind for the talk were you were you doing a Reverb talk and you're like hey here's a crazy idea or was this always the vision the original talk that I pitched was the
00:32:36 same talk I gave at lonu and then we released the video from lonu and I thought maybe I should do something different so then it took a while I was iterating few few through through a few different ideas which it's always going to be a rever focus talk but I wanted to do something a little bit out there so I started to research whether any drones could be controlled by websockets CU I thought this would be quite cool and turns out we managed it and it was quite cool it was very tickity boo if you ask
00:33:00 me um okay next question I'll give you two options one is um where within larel and your your first- party larel products is Reverb being used that's option one and option two is what types of problems like what is the shape the characteristic of the problem um that people should uh see when they're reaching for Reb so if you see this type of problem Reverb is a good fit your choice can I take both yeah all right uh so internally re Reb as I mentioned is used on Forge and Envoy not yet vapor and yesterday it was all that real-time
00:33:35 stuff on cloud was Reverb as well uh the second question I mean it depends a little bit I think if you have like cloud is the perfect example because there's loads of stuff happening B behind the scenes we're processing web hooks from GitHub we're dealing with events that are happening during deployments and we want all of that UI to feel real time and to do that with short polling with that amount of different things happening would feel pretty wasteful and so I think that kind of thing makes a lot of
00:34:02 sense but you probably got to weigh it up and see what makes sense and I'm going to give you the classic it depends it depends a good answer uh the last question came from the audience um can you tell us how to pronounce aluminum aluminum oh good one that should have been on the slide that's aluminium aluminium everybody Joe Dixon thank you that
Highlights
๐ค Warm Welcome: Joe thanks the audience for their introduction.
๐ Theory-Heavy Start: The talk begins with foundational concepts of Laravel Reverb.
๐ดโโ ๏ธ UK vs. US English: Joe clarifies terms to avoid confusion during the presentation.
๐ ๏ธ Laravel Reverb Overview: A first-party package for real-time web socket communication is introduced.
๐ Live Demo: Joe demonstrates controlling a drone live using Laravel Reverb.
๐ WebSocket Advantages: WebSockets provide a more efficient alternative to polling for real-time updates.
๐ Scalability: Reverb is designed to handle thousands of concurrent connections efficiently.
Key Insights
๐ก Real-Time Applications: Laravel Reverb is ideal for applications requiring real-time communication, enhancing user experience by minimizing latency.
๐ Efficient Communication: WebSockets maintain an open connection for data transfer, reducing the overhead compared to traditional HTTP requests.
๐ Scalability: Reverbโs architecture allows horizontal scaling with Redis for handling millions of requests seamlessly, making it suitable for high-traffic applications.
๐ค Interactive Demos: Live demonstrations, like controlling a drone, showcase the capabilities of Laravel Reverb and engage the audience effectively.
๐ ๏ธ Simplified Installation: The integration of Reverb with Laravel is streamlined, allowing easy setup through the Artisan command, enhancing developer productivity.
๐ฎ User-Friendly UI: Using modern front-end technologies (like Tailwind CSS), developers can create intuitive interfaces that leverage real-time features.
๐ Global Collaboration: Joeโs international background highlights the community aspect of Laravel, fostering collaboration across different regions and cultures.