[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"portfolio-options":3,"0cFUueFOYrchudb6Mx64pigVEiV-eo7iMOXVQ5Q8fTw":43,"_apollo:default":168},{"footerColofon":4,"siteLogo":5,"footerSocials":5,"heroRole":6,"heroName":7,"heroLede1":8,"heroLede2":9,"contactTitle":10,"contactHomeIntro":11,"contactIntro":12,"contactReplyNote":13,"registerTitle":14,"projectsTitle":15,"projectsIntro":16,"experiencesTitle":17,"experiencesIntro":18,"cvTitle":19,"cvIntro":20,"seoTitleSuffix":21,"seoDefaultDescription":22,"seoDefaultImage":5,"seoPages":23,"seoHomeTitle":5,"seoHomeDescription":5,"seoProjectsTitle":5,"seoProjectsDescription":5,"seoExperiencesTitle":5,"seoExperiencesDescription":5,"seoCvTitle":5,"seoCvDescription":5,"seoContactTitle":5,"seoContactDescription":5,"__typename":42},"\u003Cp>Deze site draait op \u003Cstrong>Nuxt\u003C\u002Fstrong>, \u003Cstrong>headless WordPress\u003C\u002Fstrong> en \u003Cstrong>Vercel\u003C\u002Fstrong>.\u003C\u002Fp>\n",null,"Full-stack developer, Nederland, remote beschikbaar","Marthijn Beilschmidt","Zes jaar ervaring in webdevelopment: bij bureaus, in loondienst en als zelfstandige.","Hier laat ik zien wat ik daarbuiten bouw: van interne dashboards tot hobbyprojecten die niemand vroeg, maar die ik toch maakte.","Laten we connecten","Vragen over m'n werk, een rol, of gewoon nieuwsgierig naar hoe iets gebouwd is? Laat het weten.","Vragen over m'n werk, een rol, of gewoon nieuwsgierig hoe iets gebouwd is? Stuur een bericht.","Meestal reageer ik binnen een werkdag.","Register","Projecten","Mijn klantwerk, interne tools en hobbyprojecten, elk met de techniek die erin zit. Filterbaar op vaardigheid.","Werkervaring","Waar ik gewerkt heb en wat ik er deed, van m'n eerste stage tot nu, als tijdlijn, met de plekken die nog doorlopen.","Curriculum Vitae","Mijn achtergrond, vaardigheden en werk op één pagina. Ook te downloaden als PDF."," - Marthijn Beilschmidt","Full-stack developer uit Nederland met zes jaar ervaring. Hier laat ik zien wat ik bouw: klantwerk, interne tools en eigen projecten.",[24,29,31,33,35,38],{"route":25,"title":26,"description":22,"noindex":27,"image":5,"__typename":28},"\u002F","Marthijn Beilschmidt - Portfolio",false,"PortfolioOptionsFieldsSeoPages",{"route":30,"title":15,"description":16,"noindex":27,"image":5,"__typename":28},"\u002Fprojects",{"route":32,"title":17,"description":18,"noindex":27,"image":5,"__typename":28},"\u002Fexperiences",{"route":34,"title":19,"description":20,"noindex":27,"image":5,"__typename":28},"\u002Fcv",{"route":36,"title":37,"description":12,"noindex":27,"image":5,"__typename":28},"\u002Fcontact","Contact",{"route":39,"title":40,"description":41,"noindex":27,"image":5,"__typename":28},"\u002Fblog","Blog","Artikelen en inzichten over webdevelopment, technologie en projecten.","PortfolioOptionsFields",{"project":44},{"id":45,"databaseId":46,"title":47,"slug":48,"date":49,"content":50,"seo":51,"featuredImage":62,"projectFields":70,"__typename":167},"cG9zdDo0NjA=",460,"OSRS Events","osrs-events","2026-09-12T12:03:59","\u003Cp>Clans in Old School RuneScape organiseren evenementen voor hun leden. OSRS Events is het gereedschap daarvoor.\u003C\u002Fp>\n\u003Cp>Er zijn vier soorten:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Een bord &#8220;slangen en ladders&#8221;\u003C\u002Fli>\n\u003Cli>Een skillrace waarin je een maand lang zoveel mogelijk ervaring in één vaardigheid haalt\u003C\u002Fli>\n\u003Cli>Een droprace op basis van hoeveel bazen je verslaat\u003C\u002Fli>\n\u003Cli>Een bingokaart van 3×3 tot 10×10.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Alleen dat eerste type heeft een bord. Daarom staan een evenement en een bord in het datamodel los van elkaar, anders sleep je bij drie van de vier soorten iets mee wat niet bestaat.\u003C\u002Fp>\n\u003Ch2>Waarom\u003C\u002Fh2>\n\u003Cp>Het idee komt rechtstreeks uit mijn eigen clan. Een evenement organiseren kostte telkens onnodig veel moeite: de planning ging handmatig, de standen werden bijgehouden waar het uitkwam en voor bingo trokken we er willekeurige bingosites bij die verder nergens op aansloten. Alles stond los van elkaar en niets was verbonden aan het spel zelf.\u003C\u002Fp>\n\u003Cp>Ik wilde één ding waarin het allemaal zit en ben toen begonnen met bouwen.\u003C\u002Fp>\n\u003Cp>Daar komt bij dat het om standen gaat en standen roepen de vraag op of ze kloppen. Iemand zegt dat hij die ervaring heeft gehaald; iemand anders moet dat geloven of gaan controleren. Deze applicatie haalt de cijfers zelf op. Elk account draagt een RuneScape-gebruikersnaam met zich mee die wordt bij aanmelden gevraagd. Als je geen gebruikersnaam hebt, wordt je naar een pagina met één invoerveld gestuurd voordat hij zich kan aanmelden voor een evenement. Zonder die naam is er niets op te zoeken, want de officiële scorelijsten werken op accountnaam.\u003C\u002Fp>\n\u003Ch2>Hoe het werkt\u003C\u002Fh2>\n\u003Cp>Laravel 13 met Inertia en Vue 3. Geen aparte API-laag: controllers geven een Inertia-response terug en Vue neemt het over. Rendering gaat door een langdraaiend Node-proces zodat de pagina&#8217;s server-side gerenderd binnenkomen. Sleutels zijn overal UUID&#8217;s, ook in de rollen- en rechtenlaag, waarvoor de standaardimplementatie is overschreven.\u003C\u002Fp>\n\u003Cp>Elk evenementtype heeft een eigen live-kanaal via Server-Sent Events. De pagina opent één verbinding en de server duwt er veranderingen doorheen zodat je live iets ziet gebeuren. Dit werkt vrijwel hetzelfde als WebSockets, echter is dat bedoeld om ook pakketten vanuit jou naar de server en anderen te sturen. Dat is hier niet de bedoeling.\u003C\u002Fp>\n\u003Ch2>Het is een keer helemaal opnieuw gebouwd\u003C\u002Fh2>\n\u003Cp>Dit begon als een PoC. Een Nuxt-frontend en een losse Node-backend met GraphQL en Prisma, allebei op Vercel, vooral om te zien of ik dat kon. Dat lukte en toen groeide het door tot iets dat andere spelers echt zouden willen gebruiken.\u003C\u002Fp>\n\u003Cp>Op dat punt begonnen twee dingen te wringen. Twee applicaties naast elkaar onderhouden viel zwaarder uit dan gedacht en de boel was traag genoeg dat het opviel. Voor een demo maakt dat niets uit. Voor een site waar clanleden elke dag hun stand op bekijken wel.\u003C\u002Fp>\n\u003Cp>Dus is de hele applicatie van stack gewisseld: Laravel met Inertia, één codebase in plaats van twee. De oude versie is niet weggegooid maar naar een \u003Ccode>stale\u003C\u002Fcode>-map verhuisd en daarna is alles er stuk voor stuk overheen gezet.\u003C\u002Fp>\n\u003Cp>Dat het in een paar dagen kon, kwam doordat het eerst op een aparte branch is uitgeprobeerd in plaats van meteen in de hoofdlijn.\u003C\u002Fp>\n\u003Ch2>Kernpunten waar ik trots op ben\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Server-Sent Events met één kanaal per eventtype.\u003C\u002Fstrong> De pagina opent één verbinding en de server duwt er veranderingen doorheen zodat je live iets ziet gebeuren. Per kanaal een eventtype toevoegen en de stream versleutelen voor mensen die geen toegang hebben.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Diepe Discord-koppeling\u003C\u002Fstrong> voor het afschermen van evenementen en het indelen van teams. De login haalt ook de servers op waar je lid van bent, waardoor een bord lidmaatschap van een specifieke Discord-server kan worden geëist.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Uitnodigingssysteem met shortcode, vervaldatum en gebruikslimiet.\u003C\u002Fstrong> Het verschil tussen een link die je in een Discord-kanaal durft te plakken en een link die eeuwig blijft werken.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Claim-en-goedkeurflow met eigen moderatorrollen\u003C\u002Fstrong>, voor eventtypes waarbij niets automatisch te verifiëren valt. Een speler claimt, een moderator keurt goed.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>PWA-installatie met servermeldingen\u003C\u002Fstrong> bij gebeurtenissen in een evenement, zodat je niet zelf hoeft te blijven kijken.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>De meest feature-rijke UI van alles wat ik heb gebouwd.\u003C\u002Fstrong> Veel schermen, veel staten, veel iteraties en het moest tegelijk overzichtelijk blijven voor mensen die er één keer per dag even in kijken.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Aankomend: een eigen RuneLite-plugin\u003C\u002Fstrong>, zodat je voortgang zichtbaar wordt in de client waarin vrijwel iedereen het spel speelt.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Wat er nog niet in zit\u003C\u002Fh2>\n\u003Cp>Er komt nog een eigen RuneLite-plugin, de software waar vrijwel iedereen het spel mee speelt, zodat je voortgang wordt geautomatiseerd. Dat wordt tegelijk het leerstuk: ik heb eerder een uitbreiding op een bestaande RuneLite-plugin gebouwd en dat beviel goed genoeg om er zelf een plug-in te bouwen.\u003C\u002Fp>\n\u003Cp>Verder staan er genoeg dingen op de planning. De voortgang is te volgen op \u003Ca href=\"http:\u002F\u002Fosrs-events.com\">osrs-events.com\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch2>Wat het opleverde\u003C\u002Fh2>\n\u003Cp>Dezelfde applicatie twee keer gebouwd op twee stacks en dat is een vergelijking die bijna niemand kan maken. Verder is dit het project waar ik met Server-Sent Events heb gewerkt in plaats van met websockets via Reverb, wat een heel ander soort oplossing is voor hetzelfde probleem. En veel nauwe koppelingen plus veel UX-iteraties, omdat het tegelijk gebruiksvriendelijk en visueel rijk moest zijn.\u003C\u002Fp>\n",{"title":52,"metaDesc":53,"canonical":53,"opengraphTitle":52,"opengraphDescription":54,"metaRobotsNoindex":55,"metaRobotsNofollow":56,"opengraphImage":57,"__typename":61},"OSRS Events - WP - Admin","","Clans in Old School RuneScape organiseren evenementen voor hun leden. OSRS Events is het gereedschap daarvoor. Er zijn vier soorten: Een bord “slangen en ladders” Een skillrace waarin je een maand lang zoveel mogelijk ervaring in één vaardigheid haalt Een droprace op basis van hoeveel bazen je verslaat Een bingokaart van 3×3 tot 10×10. Alleen … Continued","noindex","follow",{"sourceUrl":58,"altText":59,"__typename":60},"https:\u002F\u002Fadmin.marthijnbeilschmidt.nl\u002Fapp\u002Fuploads\u002F2026\u002F09\u002Fbanner-osrs-events.webp","Het logo van OSRS Events op een vlak in de merkkleur.","MediaItem","PostTypeSEO",{"node":63,"__typename":69},{"altText":59,"mediaItemUrl":58,"mediaType":64,"mediaDetails":65,"__typename":60},"image",{"width":66,"height":67,"__typename":68},1600,900,"MediaDetails","NodeWithFeaturedImageToMediaItemConnectionEdge",{"description":71,"projectType":72,"client":5,"duration":74,"headerImage":75,"gallery":82,"skills":124,"liveWebsite":165,"__typename":166},"Eventborden voor RuneScape-clans, waarbij de standen zichzelf verifiëren tegen de officiële scorelijsten.",[73],"prive","Doorlopend",{"node":76,"__typename":81},{"altText":77,"mediaItemUrl":78,"mediaType":64,"mediaDetails":79,"__typename":60},"Een eventbord in OSRS Events, met de tussenstand van de deelnemers.","https:\u002F\u002Fadmin.marthijnbeilschmidt.nl\u002Fapp\u002Fuploads\u002F2026\u002F09\u002Fbord-desktop.webp",{"width":66,"height":80,"__typename":68},1000,"AcfMediaItemConnectionEdge",{"nodes":83,"__typename":123},[84,90,96,104,111,117],{"id":85,"databaseId":86,"altText":87,"mediaItemUrl":88,"mediaType":64,"mediaDetails":89,"__typename":60},"cG9zdDo0NjI=",462,"Een bingobord in OSRS Events, met de vakjes die een clan kan afstrepen.","https:\u002F\u002Fadmin.marthijnbeilschmidt.nl\u002Fapp\u002Fuploads\u002F2026\u002F09\u002Fbingo-desktop.webp",{"width":66,"height":80,"__typename":68},{"id":91,"databaseId":92,"altText":93,"mediaItemUrl":94,"mediaType":64,"mediaDetails":95,"__typename":60},"cG9zdDo0NjQ=",464,"Het evenementenoverzicht van OSRS Events op een breed scherm.","https:\u002F\u002Fadmin.marthijnbeilschmidt.nl\u002Fapp\u002Fuploads\u002F2026\u002F09\u002Fevenementen-desktop.webp",{"width":66,"height":80,"__typename":68},{"id":97,"databaseId":98,"altText":99,"mediaItemUrl":100,"mediaType":64,"mediaDetails":101,"__typename":60},"cG9zdDo0NjU=",465,"Het evenementenoverzicht van OSRS Events op een telefoon.","https:\u002F\u002Fadmin.marthijnbeilschmidt.nl\u002Fapp\u002Fuploads\u002F2026\u002F09\u002Fevenementen-mobile.webp",{"width":102,"height":103,"__typename":68},1170,2532,{"id":105,"databaseId":106,"altText":107,"mediaItemUrl":108,"mediaType":64,"mediaDetails":109,"__typename":60},"cG9zdDo0NjY=",466,"De voorpagina van OSRS Events, met daaronder een levend bord als voorbeeld.","https:\u002F\u002Fadmin.marthijnbeilschmidt.nl\u002Fapp\u002Fuploads\u002F2026\u002F09\u002Fhome-desktop-2.webp",{"width":66,"height":110,"__typename":68},2111,{"id":112,"databaseId":113,"altText":114,"mediaItemUrl":115,"mediaType":64,"mediaDetails":116,"__typename":60},"cG9zdDo0Njc=",467,"Dezelfde voorpagina in lichte modus.","https:\u002F\u002Fadmin.marthijnbeilschmidt.nl\u002Fapp\u002Fuploads\u002F2026\u002F09\u002Fhome-licht-desktop.webp",{"width":66,"height":110,"__typename":68},{"id":118,"databaseId":119,"altText":120,"mediaItemUrl":121,"mediaType":64,"mediaDetails":122,"__typename":60},"cG9zdDo0Njg=",468,"Een skillrace in OSRS Events, waarvan de standen zich verifieren tegen de officiele scorelijsten.","https:\u002F\u002Fadmin.marthijnbeilschmidt.nl\u002Fapp\u002Fuploads\u002F2026\u002F09\u002Fskillrace-desktop.webp",{"width":66,"height":80,"__typename":68},"AcfMediaItemConnection",{"nodes":125,"__typename":164},[126,135,140,146,152,158],{"id":127,"databaseId":128,"title":129,"skillFields":130,"__typename":134},"cG9zdDoyOTE=",291,"Laravel",{"icon":131,"description":132,"__typename":133},"simple-icons:laravel","Wanneer je applicatie* aanzienlijk complexer wordt en vanaf de basis moet worden opgebouwd.","SkillFields","Skill",{"id":136,"databaseId":137,"title":138,"skillFields":139,"__typename":134},"cG9zdDo0MDY=",406,"Inertia",{"icon":5,"description":5,"__typename":133},{"id":141,"databaseId":142,"title":143,"skillFields":144,"__typename":134},"cG9zdDozOTI=",392,"Vue 3",{"icon":145,"description":5,"__typename":133},"logos:vue",{"id":147,"databaseId":148,"title":149,"skillFields":150,"__typename":134},"cG9zdDozMDk=",309,"PHP",{"icon":151,"description":5,"__typename":133},"simple-icons:php",{"id":153,"databaseId":154,"title":155,"skillFields":156,"__typename":134},"cG9zdDozOTE=",391,"Tailwind CSS",{"icon":157,"description":5,"__typename":133},"logos:tailwindcss-icon",{"id":159,"databaseId":160,"title":161,"skillFields":162,"__typename":134},"cG9zdDozOTA=",390,"MySQL",{"icon":163,"description":5,"__typename":133},"logos:mysql-icon","AcfContentNodeConnection","https:\u002F\u002Fosrs-events.com","ProjectFields","Project",{}]