2:I[6868,["173","static/chunks/173-424fa9be91923976.js","49","static/chunks/49-d41f7eeba1de0524.js","369","static/chunks/app/blog/combining-vector-and-shaders-for-2d-art/page-eb7b223015a0691c.js"],"SolandraStacked"] 3:I[9275,[],""] 4:I[1343,[],""] 5:I[2650,["173","static/chunks/173-424fa9be91923976.js","231","static/chunks/231-1e7bbdc770c66fc3.js","521","static/chunks/521-7266f541e89dccd7.js","185","static/chunks/app/layout-1a67d20efb7fbcee.js"],"Providers"] 6:I[7848,["173","static/chunks/173-424fa9be91923976.js","231","static/chunks/231-1e7bbdc770c66fc3.js","521","static/chunks/521-7266f541e89dccd7.js","185","static/chunks/app/layout-1a67d20efb7fbcee.js"],"Header"] 7:I[231,["173","static/chunks/173-424fa9be91923976.js","49","static/chunks/49-d41f7eeba1de0524.js","231","static/chunks/231-1e7bbdc770c66fc3.js","847","static/chunks/847-d03f1a8cba4db626.js","404","static/chunks/app/blog/page-527c30508ff17cd7.js"],""] 0:["zCwPuQV_WbLEohhlcHJz-",[[["",{"children":["blog",{"children":["combining-vector-and-shaders-for-2d-art",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",true],["",{"children":["blog",{"children":["combining-vector-and-shaders-for-2d-art",{"children":["__PAGE__",{},[["$L1",["$","div",null,{"className":"sm:px-8 mt-16 lg:mt-32","children":["$","div",null,{"className":"mx-auto w-full max-w-7xl lg:px-8","children":["$","div",null,{"className":"relative px-4 sm:px-8 lg:px-12","children":["$","div",null,{"className":"mx-auto max-w-2xl lg:max-w-5xl","children":["$","div",null,{"className":"xl:relative","children":["$","div",null,{"className":"mx-auto max-w-2xl","children":["$","article",null,{"children":[["$","header",null,{"className":"flex flex-col","children":[["$","h1",null,{"className":"mt-6 text-4xl font-bold tracking-tight text-zinc-800 sm:text-5xl dark:text-zinc-100","children":"Combining Vector Graphics and Shaders with Solandra"}],["$","time",null,{"dateTime":"2024-06-15T17:17:58.655Z","className":"order-first flex items-center text-base text-zinc-400 dark:text-zinc-500","children":[["$","span",null,{"className":"h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500"}],["$","span",null,{"className":"ml-3","children":"15 June 2024"}]]}]]}],["$","div",null,{"className":"mt-8 prose dark:prose-invert","data-mdx-content":true,"children":[["$","p",null,{"children":["Solandra 0.19 has APIs for rendering both fragment shaders (GLSL) and ",["$","em",null,{"children":"Solandra Sketches"}]," to offscreen Canvases in order to create images. These images can be combined within a Solandra sketch. Let's see how this works with a simple example."]}],"\n",["$","$L2",null,{}],"\n",["$","p",null,{"children":"How was this done? First let's render a fragment shader. Fragment shaders render per-pixel. So we don't describe shapes and lines, we instead must give a colour value for every single pixel. These values are calculated in a very low level way in the C-like GLSL. We have to figure out a 4d vector for each pixel where the first three values are the RGB values and the last is the alpha value. We can't even use values in our other code directly, we have to pass them in as uniforms."}],"\n",["$","p",null,{"children":["Solandra takes care of setting up the context and wiring up the ",["$","code",null,{"children":"u_resolution"}]," uniform. To render something all you need to do is pass in a ",["$","code",null,{"children":"shader"}]," which contains a ",["$","code",null,{"children":"main"}]," function that writes to ",["$","code",null,{"children":"gl_FragColor"}],". This creates a simple gradient where the red and green values are the relative x and y coordinates of the pixel."]}],"\n",["$","pre",null,{"className":"language-ts","children":["$","code",null,{"className":"language-ts","children":[["$","span",null,{"className":"token keyword","children":"const"}]," shaderImage ",["$","span",null,{"className":"token operator","children":"="}]," ",["$","span",null,{"className":"token function","children":"renderShader"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}],"\n shader",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token comment","children":"/* glsl */"}]," ",["$","span",null,{"className":"token template-string","children":[["$","span",null,{"className":"token template-punctuation string","children":"`"}],["$","span",null,{"className":"token string","children":"\n void main() { \n vec2 r_pos = gl_FragCoord.xy / u_resolution; \n gl_FragColor = vec4(r_pos.x, r_pos.y, 0.4, 1.0); \n }\n "}],["$","span",null,{"className":"token template-punctuation string","children":"`"}]]}],["$","span",null,{"className":"token punctuation","children":","}],"\n",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}]}],"\n",["$","p",null,{"children":["Now let's render something with Solandra offscreen. With Solandra we have access to much more powerful APIs and we don't typically care about individual pixels, we draw the entire scene. We will iterate over a set of ",["$","code",null,{"children":"PoissonDiskPoints"}],", generating a hue from the index and drawing a circle."]}],"\n",["$","pre",null,{"className":"language-ts","children":["$","code",null,{"className":"language-ts","children":[["$","span",null,{"className":"token keyword","children":"const"}]," solImage ",["$","span",null,{"className":"token operator","children":"="}]," ",["$","span",null,{"className":"token function","children":"render"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}],"\n w",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token number","children":"1024"}],["$","span",null,{"className":"token punctuation","children":","}],"\n h",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token number","children":"1024"}],["$","span",null,{"className":"token punctuation","children":","}],"\n ",["$","span",null,{"className":"token function-variable function","children":"sketch"}],["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token punctuation","children":"("}],"s",["$","span",null,{"className":"token punctuation","children":")"}]," ",["$","span",null,{"className":"token arrow operator","children":"=>"}]," ",["$","span",null,{"className":"token punctuation","children":"{"}],"\n s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"forPoissonDiskPoints"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}]," minDist",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token number","children":"0.05"}]," ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token punctuation","children":"("}],"pt",["$","span",null,{"className":"token punctuation","children":","}]," i",["$","span",null,{"className":"token punctuation","children":")"}]," ",["$","span",null,{"className":"token arrow operator","children":"=>"}]," ",["$","span",null,{"className":"token punctuation","children":"{"}],"\n s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"setFillColor"}],["$","span",null,{"className":"token punctuation","children":"("}],"i ",["$","span",null,{"className":"token operator","children":"%"}]," ",["$","span",null,{"className":"token number","children":"360"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token number","children":"80"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token number","children":"70"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token number","children":"0.7"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"fill"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token keyword","children":"new"}]," ",["$","span",null,{"className":"token class-name","children":["$","span",null,{"className":"token maybe-class-name","children":"Circle"}]}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}]," at",["$","span",null,{"className":"token operator","children":":"}]," pt",["$","span",null,{"className":"token punctuation","children":","}]," r",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token number","children":"0.025"}]," ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":","}],"\n",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}]}],"\n",["$","p",null,{"children":["Now let's combine these. We use ",["$","code",null,{"children":"withClipping"}]," to clip the shader to a circle and ",["$","code",null,{"children":"withBlendMode"}]," to overlay the Solandra image on top of the shader in an interesting way."]}],"\n",["$","pre",null,{"className":"language-ts","children":["$","code",null,{"className":"language-ts","children":["s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"background"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token number","children":"20"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token number","children":"40"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token number","children":"40"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n\ns",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"withClipping"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token keyword","children":"new"}]," ",["$","span",null,{"className":"token class-name","children":["$","span",null,{"className":"token maybe-class-name","children":"Circle"}]}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}]," at",["$","span",null,{"className":"token operator","children":":"}]," s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token property-access","children":"meta"}],["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token property-access","children":"center"}],["$","span",null,{"className":"token punctuation","children":","}]," r",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token number","children":"0.4"}]," ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":")"}]," ",["$","span",null,{"className":"token arrow operator","children":"=>"}]," ",["$","span",null,{"className":"token punctuation","children":"{"}],"\n s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"drawImage"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}]," image",["$","span",null,{"className":"token operator","children":":"}]," shaderImage ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n\ns",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"withBlendMode"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token string","children":"'overlay'"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":")"}]," ",["$","span",null,{"className":"token arrow operator","children":"=>"}]," ",["$","span",null,{"className":"token punctuation","children":"{"}],"\n s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"drawImage"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}]," image",["$","span",null,{"className":"token operator","children":":"}]," solImage ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}]}],"\n",["$","h2",null,{"children":"Using Images from Shaders"}],"\n",["$","p",null,{"children":"We can also pass images into Solandra shader rendering. Let's first create a simple image of 'snow fall'. A black background and some white circles."}],"\n",["$","pre",null,{"className":"language-ts","children":["$","code",null,{"className":"language-ts","children":[["$","span",null,{"className":"token keyword","children":"const"}]," solImage ",["$","span",null,{"className":"token operator","children":"="}]," ",["$","span",null,{"className":"token function","children":"render"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}],"\n w",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token number","children":"1024"}],["$","span",null,{"className":"token punctuation","children":","}],"\n h",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token number","children":"1024"}],["$","span",null,{"className":"token punctuation","children":","}],"\n ",["$","span",null,{"className":"token function-variable function","children":"sketch"}],["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token punctuation","children":"("}],"s",["$","span",null,{"className":"token punctuation","children":")"}]," ",["$","span",null,{"className":"token arrow operator","children":"=>"}]," ",["$","span",null,{"className":"token punctuation","children":"{"}],"\n s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"background"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token number","children":"0"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token number","children":"0"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token number","children":"0"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"setFillColor"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token number","children":"0"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token number","children":"0"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token number","children":"100"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"forPoissonDiskPoints"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}]," minDist",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token number","children":"0.05"}]," ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":","}]," ",["$","span",null,{"className":"token punctuation","children":"("}],"pt",["$","span",null,{"className":"token punctuation","children":")"}]," ",["$","span",null,{"className":"token arrow operator","children":"=>"}]," ",["$","span",null,{"className":"token punctuation","children":"{"}],"\n s",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"fill"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token keyword","children":"new"}]," ",["$","span",null,{"className":"token class-name","children":["$","span",null,{"className":"token maybe-class-name","children":"Circle"}]}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}]," at",["$","span",null,{"className":"token operator","children":":"}]," pt",["$","span",null,{"className":"token punctuation","children":","}]," r",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token number","children":"0.01"}]," ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":","}],"\n",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}]}],"\n",["$","p",null,{"children":["Okay how do we pass into a shader? That is very simple, the ",["$","code",null,{"children":"renderShader"}]," function takes an optional object of images."]}],"\n",["$","pre",null,{"className":"language-ts","children":["$","code",null,{"className":"language-ts","children":["images",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token punctuation","children":"{"}],"\n solImage\n",["$","span",null,{"className":"token punctuation","children":"}"}],"\n"]}]}],"\n",["$","p",null,{"children":["How do we use these images? With ",["$","code",null,{"children":"texture2D"}]," we can sample the image at a given coordinate. We can now do fancy per-pixel effects on the original images, so for example here we are taking the red, green and blue values from slightly offset locations to create an effect. The ",["$","code",null,{"children":".r"}]," and so on just means take the first colour from this pixel i.e red."]}],"\n",["$","pre",null,{"className":"language-ts","children":["$","code",null,{"className":"language-ts","children":[["$","span",null,{"className":"token keyword","children":"const"}]," shaderImage ",["$","span",null,{"className":"token operator","children":"="}]," ",["$","span",null,{"className":"token function","children":"renderShader"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}],"\n shader",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token comment","children":"/* glsl */"}]," ",["$","span",null,{"className":"token template-string","children":[["$","span",null,{"className":"token template-punctuation string","children":"`"}],["$","span",null,{"className":"token string","children":"\n void main() { \n vec2 r_pos = gl_FragCoord.xy / u_resolution; \n\n vec2 r_pos_r = r_pos - vec2(0.01, 0.0);\n vec2 r_pos_g = r_pos - vec2(0.0, 0.01);\n vec2 r_pos_b = r_pos - vec2(0.01, 0.01);\n\n float r = texture2D(solImage, r_pos_r).r;\n float g = texture2D(solImage, r_pos_g).g;\n float b = texture2D(solImage, r_pos_b).b;\n\n gl_FragColor = vec4(r, g, b, 1.0);\n }\n "}],["$","span",null,{"className":"token template-punctuation string","children":"`"}]]}],["$","span",null,{"className":"token punctuation","children":","}],"\n images",["$","span",null,{"className":"token operator","children":":"}]," ",["$","span",null,{"className":"token punctuation","children":"{"}]," solImage ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":","}],"\n",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n\n",["$","span",null,{"className":"token comment","children":"// Actually render it:"}],"\ns",["$","span",null,{"className":"token punctuation","children":"."}],["$","span",null,{"className":"token method function property-access","children":"drawImage"}],["$","span",null,{"className":"token punctuation","children":"("}],["$","span",null,{"className":"token punctuation","children":"{"}]," image",["$","span",null,{"className":"token operator","children":":"}]," shaderImage ",["$","span",null,{"className":"token punctuation","children":"}"}],["$","span",null,{"className":"token punctuation","children":")"}],"\n"]}]}],"\n",["$","$L2",null,{"index":1}]]}]]}]}]}]}]}]}]}]],null],null]},["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","blog","children","combining-vector-and-shaders-for-2d-art","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}],null]},["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","blog","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}],null]},[["$","html",null,{"lang":"en","className":"h-full antialiased","suppressHydrationWarning":true,"children":["$","body",null,{"className":"flex h-full bg-zinc-200 dark:bg-black","children":["$","$L5",null,{"children":["$","div",null,{"className":"flex w-full","children":[["$","div",null,{"className":"fixed inset-0 flex justify-center sm:px-8","children":["$","div",null,{"className":"flex w-full max-w-7xl lg:px-8","children":["$","div",null,{"className":"w-full bg-white ring-1 ring-zinc-100 dark:bg-zinc-900 dark:ring-zinc-300/20"}]}]}],["$","div",null,{"className":"relative flex w-full flex-col","children":[["$","$L6",null,{}],["$","main",null,{"className":"flex-auto","children":["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":["$","div",null,{"className":"sm:px-8 flex h-full items-center pt-16 sm:pt-32","children":["$","div",null,{"className":"mx-auto w-full max-w-7xl lg:px-8","children":["$","div",null,{"className":"relative px-4 sm:px-8 lg:px-12","children":["$","div",null,{"className":"mx-auto max-w-2xl lg:max-w-5xl","children":["$","div",null,{"className":"flex flex-col items-center","children":[["$","p",null,{"className":"text-base font-semibold text-zinc-400 dark:text-zinc-500","children":"404"}],["$","h1",null,{"className":"mt-4 text-4xl font-bold tracking-tight text-rose-800 sm:text-5xl dark:text-rose-100","children":"Page not found"}],["$","p",null,{"className":"mt-4 text-base text-zinc-600 dark:text-zinc-400","children":"Sorry, we couldn’t find the page you’re looking for."}],["$","p",null,{"className":"mt-4 text-base text-zinc-600 dark:text-zinc-400","children":["You may want to try"," ",["$","a",null,{"href":"https://archive.amimetic.co.uk","className":"font-semibold text-rose-600 hover:text-rose-500","children":"the old version of this website"}]," ","as not everything was included here."]}],["$","$L7",null,{"className":"inline-flex items-center gap-2 justify-center rounded-md py-2 px-3 text-sm outline-offset-2 transition active:transition-none bg-zinc-50 font-medium text-zinc-900 hover:bg-zinc-100 active:bg-zinc-100 active:text-zinc-900/60 dark:bg-zinc-800/50 dark:text-zinc-300 dark:hover:bg-zinc-800 dark:hover:text-zinc-50 dark:active:bg-zinc-800/50 dark:active:text-zinc-50/70 mt-4","href":"/","children":"Go back home"}]]}]}]}]}]}],"notFoundStyles":[],"styles":null}]}],["$","footer",null,{"className":"mt-32 flex-none","children":["$","div",null,{"className":"sm:px-8","children":["$","div",null,{"className":"mx-auto w-full max-w-7xl lg:px-8","children":["$","div",null,{"className":"border-t border-zinc-100 pb-16 pt-10 dark:border-zinc-700/40","children":["$","div",null,{"className":"relative px-4 sm:px-8 lg:px-12","children":["$","div",null,{"className":"mx-auto max-w-2xl lg:max-w-5xl","children":["$","div",null,{"className":"flex flex-col items-center justify-between gap-6 sm:flex-row","children":[["$","div",null,{"className":"flex flex-wrap justify-center gap-x-6 gap-y-1 text-sm font-medium text-zinc-800 dark:text-zinc-200","children":[["$","$L7",null,{"href":"/about","className":"transition hover:text-sky-500 dark:hover:text-sky-400","children":"About"}],["$","$L7",null,{"href":"/blog","className":"transition hover:text-sky-500 dark:hover:text-sky-400","children":"Writing"}],["$","$L7",null,{"href":"/projects","className":"transition hover:text-sky-500 dark:hover:text-sky-400","children":"Projects"}]]}],["$","p",null,{"className":"text-sm text-zinc-400 dark:text-zinc-500","children":["© ",2024," James Porter"]}]]}]}]}]}]}]}]}]]}]]}]}]}]}],null],null],[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/3dda7107ffc2265a.css","precedence":"next","crossOrigin":"$undefined"}]],"$L8"]]]] 8:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Combining Vector Graphics and Shaders with Solandra - James Porter"}],["$","meta","3",{"name":"description","content":"Projects and thoughts from James Porter"}]] 1:null