{"id":28441,"date":"2022-05-13T07:21:16","date_gmt":"2022-05-13T07:21:16","guid":{"rendered":"http:\/\/make-a-custom-camera-filter-in-react"},"modified":"2025-02-23T14:50:13","modified_gmt":"2025-02-23T22:50:13","slug":"make-a-custom-camera-filter-in-react","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/","title":{"rendered":"Make a Custom Camera Filter in React"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>With almost everything being online, having the ability to add custom filters to your camera can be pretty useful. We\u2019re going to build a React app that lets users adjust a filter for their webcam and then save the image to Cloudinary.<\/p>\n<p>We\u2019ll be using <a href=\"https:\/\/p5js.org\/\">p5.js<\/a> to let us apply filters to a user\u2019s camera. Once you finish this project, you\u2019ll be able to start working with p5.js in all kinds of media apps.<\/p>\n<h2>Setting up the React app<\/h2>\n<p>We can use the <a href=\"https:\/\/create-react-app.dev\/docs\/adding-typescript\/\">create-react-app<\/a> command to generate a new React project with TypeScript enabled so we can be ready to add types from the beginning. Open your terminal and run the following command.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npx create-react-app camera-filter --template typescript\n<\/code><\/span><\/pre>\n<p>You should see a new folder called <code>camera-filter<\/code> and it will have a number of boilerplate files to get us started. There are a few packages we\u2019ll need to handle the camera filter and the upload to Cloudinary.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npm i p5 @types\/p5 html-to-image\n<\/code><\/span><\/pre>\n<p>These are the packages we need to make the filter for the camera. Also, if you don\u2019t have a Cloudinary account make sure you <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">create a free one<\/a> before we move forward because you\u2019ll need credentials to upload the pictures to this hosting service.<\/p>\n<p>Now we can work on a new component for our camera filter.<\/p>\n<h2>Adding the camera filter component<\/h2>\n<p>Go to the <code>src<\/code> folder at the root of your project and add a new sub-folder called <code>components<\/code>. Inside this folder, add a file called <code>CameraFilter.tsx<\/code>. This is a common file structure you\u2019ll run into with React projects to help keep things organized. Since the camera filter won\u2019t be a whole page by itself, we classify it as a component.<\/p>\n<p>Let\u2019s start building the camera filter by importing the following packages at the top of the <code>CameraFilter.tsx<\/code> file.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ CameraFilter.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { useEffect, useRef } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { toPng } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"html-to-image\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> p5 <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"p5\"<\/span>;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>These are the main packages we\u2019ll start with and we\u2019ll slowly add MUI elements as needed. Now let\u2019s add the functionality we need for p5.js to apply filters to the camera.<\/p>\n<h3>Setting up p5.js<\/h3>\n<p>Getting this working React projects can be a little tricky, but once you do it, you have a lot of power over how things are displayed on your site. This package has a lot of really interesting functionality you should check out. Make sure you have the <code>CameraFilter.tsx<\/code> file open and add the following code below the imports.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ CameraFilter.tsx<\/span>\n...\n\nlet cam: any, <span class=\"hljs-attr\">custShader<\/span>: any;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">sketch<\/span>(<span class=\"hljs-params\">p: any<\/span>) <\/span>{\n  <span class=\"hljs-comment\">\/\/ p is a reference to the p5 instance this sketch is attached to<\/span>\n  p.preload = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    custShader = p.loadShader(<span class=\"hljs-string\">\"..\/assets\/webcam.vert\"<\/span>, <span class=\"hljs-string\">\"..\/assets\/webcam.frag\"<\/span>);\n  };\n\n  p.setup = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-comment\">\/\/ shaders require WEBGL mode to work<\/span>\n    p.createCanvas(p.windowWidth, p.windowHeight, p.WEBGL);\n    p.noStroke();\n\n    <span class=\"hljs-comment\">\/\/ initialize the webcam at the window size<\/span>\n    cam = p.createCapture(p.VIDEO);\n    cam.size(p.windowWidth, p.windowHeight);\n\n    <span class=\"hljs-comment\">\/\/ hide the html element that createCapture adds to the screen<\/span>\n    cam.hide();\n  };\n\n  p.draw = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-comment\">\/\/ shader() sets the active shader with our shader<\/span>\n    p.shader(custShader);\n\n    <span class=\"hljs-comment\">\/\/ lets just send the cam to our shader as a uniform<\/span>\n    custShader.setUniform(<span class=\"hljs-string\">\"tex0\"<\/span>, cam);\n\n    <span class=\"hljs-comment\">\/\/ the size of one pixel on the screen<\/span>\n    custShader.setUniform(<span class=\"hljs-string\">\"stepSize\"<\/span>, &#91;<span class=\"hljs-number\">1.0<\/span> \/ p.width, <span class=\"hljs-number\">1.0<\/span> \/ p.height]);\n\n    <span class=\"hljs-comment\">\/\/ how far away to sample from the current pixel<\/span>\n    <span class=\"hljs-comment\">\/\/ 1 is 1 pixel away<\/span>\n    custShader.setUniform(<span class=\"hljs-string\">\"dist\"<\/span>, <span class=\"hljs-number\">3.0<\/span>);\n\n    <span class=\"hljs-comment\">\/\/ rect gives us some geometry on the screen<\/span>\n    p.rect(<span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">0<\/span>, p.width, p.height);\n  };\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This code sets some variables we need for p5 to work with the camera and a custom shader to make the filter. You can <a href=\"https:\/\/webglfundamentals.org\/webgl\/lessons\/webgl-shaders-and-glsl.html\">learn more about WebGL shaders here<\/a> so that you\u2019ll know how to make your own shaders. This could be useful if you plan on working with 3D media in your web apps.<\/p>\n<p>Next, we create the <code>sketch<\/code> function that will hold all of the methods p5 will call to get the camera filter set up and ready to use when we create a new instance of the p5 object a bit later. The <code>sketch<\/code> function implements a few methods that p5 will expect.<\/p>\n<p>In order to use our custom filter, we\u2019ll need a <code>preload<\/code> method for the p5 object. This will call the <code>loadShader<\/code> method from p5 with paths to our shader assets to make the shader ready to use. Then we have the <code>setup<\/code> method that tells p5 what to do with the DOM as soon as it loads on the page. Finally, we have the <code>draw<\/code> method which applies the shader to the camera we initiated in the <code>setup<\/code> method.<\/p>\n<h3>The camera component<\/h3>\n<p>With all of the p5 setup ready to use, we need to create the component that gets rendered in the browser. Beneath the p5 code, add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ CameraFilter.tsx<\/span>\n...\n\nexport <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">CameraFilter<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> p5ContainerRef = useRef();\n\n  useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-comment\">\/\/ On component creation, instantiate a p5 object with the sketch and container reference<\/span>\n    <span class=\"hljs-keyword\">const<\/span> p5Instance = <span class=\"hljs-keyword\">new<\/span> p5(sketch, p5ContainerRef.current);\n\n    <span class=\"hljs-comment\">\/\/ On component destruction, delete the p5 instance<\/span>\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n      p5Instance.remove();\n    };\n  }, &#91;]);\n\n  <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">submit<\/span>(<span class=\"hljs-params\">e: any<\/span>) <\/span>{\n    e.preventDefault();\n\n    <span class=\"hljs-keyword\">if<\/span> (p5ContainerRef.current === <span class=\"hljs-literal\">null<\/span>) {\n      <span class=\"hljs-keyword\">return<\/span>;\n    }\n\n    <span class=\"hljs-comment\">\/\/ @ts-ignore<\/span>\n    <span class=\"hljs-keyword\">const<\/span> dataUrl = <span class=\"hljs-keyword\">await<\/span> toPng(p5ContainerRef.current, { <span class=\"hljs-attr\">cacheBust<\/span>: <span class=\"hljs-literal\">true<\/span> });\n\n    <span class=\"hljs-keyword\">const<\/span> uploadApi = <span class=\"hljs-string\">`https:\/\/api.cloudinary.com\/v1_1\/your_cloud_name\/image\/upload`<\/span>;\n\n    <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData();\n    formData.append(<span class=\"hljs-string\">\"file\"<\/span>, dataUrl);\n    formData.append(<span class=\"hljs-string\">\"upload_preset\"<\/span>, <span class=\"hljs-string\">\"your_upload_preset\"<\/span>);\n\n    <span class=\"hljs-keyword\">await<\/span> fetch(uploadApi, {\n      <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"POST\"<\/span>,\n      <span class=\"hljs-attr\">body<\/span>: formData,\n    });\n  }\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;&gt;<\/span>\n      {\/* @ts-ignore *\/}\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"camera\"<\/span> <span class=\"hljs-attr\">ref<\/span>=<span class=\"hljs-string\">{p5ContainerRef}<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{submit}<\/span>&gt;<\/span>\n        Save picture\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/&gt;<\/span><\/span>\n  );\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Let\u2019s walk through this from the beginning. First, we use the <code>useRef<\/code> hook from React to define the HTML reference to the element we\u2019ll render the p5 shader on. Then we take advantage of the <code>effect<\/code> hook to create an instance of the p5 object using the <code>sketch<\/code> function we wrote earlier when the component is created. We also clean up and remove the p5 instance when the component is destroyed to make sure we don\u2019t have any weird behavior.<\/p>\n<p>Next, we create the <code>submit<\/code> function for when we decide to save a picture that\u2019s been altered by our filter. Inside this function, a few things happen. We check and make sure that the referenced element has something in it currently and if it doesn\u2019t we just return from the function. If it <em>does<\/em> have something in it, then we use the <code>html-to-image<\/code> package to create a PNG file for the filtered image.<\/p>\n<p>After we have the PNG file, then we make a variable that holds the API connection string to Cloudinary. Make sure you update this to use your own cloud name so that the images will go to your Cloudinary account. Next, we create a new <code>FormData<\/code> object that will hold all of the values Cloudinary needs to accept our upload programmatically. You\u2019ll also need to update the upload preset to match your Cloudinary account. You can find this value in <a href=\"\">your account settings<\/a>.<\/p>\n<p>The last thing we do in the <code>submit<\/code> function is call the <code>fetch<\/code> API to send our filtered image to Cloudinary. The only things remaining for this component are the actually rendered elements.<\/p>\n<p>As you can see, there isn\u2019t much that gets rendered on the page. We have the <code>&lt;div&gt;<\/code> that the p5 instance references and a button that calls the <code>submit<\/code> function when we want to save an image. All that\u2019s left for our camera filter is defining the shader files that define what the filter does to the image.<\/p>\n<h2>Writing shader files<\/h2>\n<p>As we mentioned earlier, you can <a href=\"https:\/\/webglfundamentals.org\/webgl\/lessons\/webgl-shaders-and-glsl.html\">learn more about WebGL shaders here<\/a> and I highly recommend you take a look because understanding what\u2019s happening in these files is important. We aren\u2019t going to do a deep dive into these files because it takes some understanding of topics outside of the scope of this tutorial. We <em>will<\/em> however create a couple of files to make our custom shader.<\/p>\n<p>In the <code>src<\/code> directory, add a new sub-directory called <code>assets<\/code>. Inside that folder, make two new files: <code>webcam.vert<\/code> and <code>webcam.frag<\/code>. These will make our custom shader. Add the following code to the <code>webcam.vert<\/code> file.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>\/\/ webcam.vert\n\nattribute vec3 aPosition;\nattribute vec2 aTexCoord;\n\nvarying vec2 vTexCoord;\n\nvoid main() {\n  vTexCoord = aTexCoord;\n\n  \/\/ copy the position data into a vec4, using 1.0 as the w component\n  vec4 positionVec4 = vec4(aPosition, 1.0);\n  positionVec4.xy = positionVec4.xy * 2.0 - 1.0;\n\n  \/\/ send the vertex information on to the fragment shader\n  gl_Position = positionVec4;\n}\n<\/code><\/pre>\n<p>Then open the <code>webcam.frag<\/code> file and add this code.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>\/\/ webcam.frag\n\nprecision mediump float;\n\n\/\/ our texcoords from the vertex shader\nvarying vec2 vTexCoord;\n\n\/\/ the texture that we want to manipulate\nuniform sampler2D tex0;\n\n\/\/ how big of a step to take. 1.0 \/ width = 1 texel\n\/\/ doing this math in p5 saves a little processing power\nuniform vec2 stepSize;\nuniform float dist;\n\n\/\/ an array with 9 vec2's\n\/\/ each index in the array will be a step in a different direction around a pixel\n\/\/ upper left, upper middle, upper right\n\/\/ middle left, middle, middle right\n\/\/ lower left, lower middle, lower right\nvec2 offset[9];\n\n\/\/ the convolution kernel we will use\n\/\/ different kernels produce different effects\n\/\/ we can do things like, emboss, sharpen, blur, etc.\nfloat kernel[9];\n\n\/\/ the sum total of all the values in the kernel\nfloat kernelWeight = 0.0;\n\n\/\/ our final convolution value that will be rendered to the screen\nvec4 conv = vec4(0.0);\n\nvoid main(){\n\n\tvec2 uv = vTexCoord;\n  \/\/ flip the y uvs\n  uv.y = 1.0 - uv.y;\n\n  \/\/ different values in the kernels produce different effects\n  \/\/ take a look here for some more examples https:\/\/en.wikipedia.org\/wiki\/Kernel_(image_processing) or https:\/\/docs.gimp.org\/en\/plug-in-convmatrix.html\n\n  \/\/ here are a few examples, try uncommenting them to see how they affect the image\n\n  \/\/ emboss kernel\n  kernel[0] = -2.0; kernel[1] = -1.0; kernel[2] = 0.0;\n  kernel[3] = -1.0; kernel[4] = 1.0; kernel[5] = 1.0;\n  kernel[6] = 0.0; kernel[7] = 1.0; kernel[8] = 2.0;\n\n  \/\/ sharpen kernel\n  \/\/ kernel[0] = -1.0; kernel[1] = 0.0; kernel[2] = -1.0;\n  \/\/ kernel[3] = 0.0; kernel[4] = 5.0; kernel[5] = 0.0;\n  \/\/ kernel[6] = -1.0; kernel[7] = 0.0; kernel[8] = -1.0;\n\n  \/\/ gaussian blur kernel\n  \/\/ kernel[0] = 1.0; kernel[1] = 2.0; kernel[2] = 1.0;\n  \/\/ kernel[3] = 2.0; kernel[4] = 4.0; kernel[5] = 2.0;\n  \/\/ kernel[6] = 1.0; kernel[7] = 2.0; kernel[8] = 1.0;\n\n  \/\/ edge detect kernel\n  \/\/ kernel[0] = -1.0; kernel[1] = -1.0; kernel[2] = -1.0;\n  \/\/ kernel[3] = -1.0; kernel[4] = 8.0; kernel[5] = -1.0;\n  \/\/ kernel[6] = -1.0; kernel[7] = -1.0; kernel[8] = -1.0;\n\n\toffset[0] = vec2(-stepSize.x, -stepSize.y); \/\/ top left\n\toffset[1] = vec2(0.0, -stepSize.y); \/\/ top middle\n\toffset[2] = vec2(stepSize.x, -stepSize.y); \/\/ top right\n\toffset[3] = vec2(-stepSize.x, 0.0); \/\/ middle left\n\toffset[4] = vec2(0.0, 0.0); \/\/middle\n\toffset[5] = vec2(stepSize.x, 0.0); \/\/middle right\n\toffset[6] = vec2(-stepSize.x, stepSize.y); \/\/bottom left\n\toffset[7] = vec2(0.0, stepSize.y); \/\/bottom middle\n\toffset[8] = vec2(stepSize.x, stepSize.y); \/\/bottom right\n\n\tfor(int i = 0; i&lt;9; i++){\n\t\t\/\/sample a 3x3 grid of pixels\n\t\tvec4 color = texture2D(tex0, uv + offset[i]*dist);\n\n    \/\/ multiply the color by the kernel value and add it to our conv total\n\t\tconv += color * kernel[i];\n\n    \/\/ keep a running tally of the kernel weights\n\t\tkernelWeight += kernel[i];\n\t}\n\n  \/\/ normalize the convolution by dividing by the kernel weight\n  conv.rgb \/= kernelWeight;\n\n\tgl_FragColor = vec4(conv.rgb, 1.0);\n}\n<\/code><\/pre>\n<p>This will create a convolution kernel effect on the camera, giving us our custom filter. You can find the source code for this shader and others in <a href=\"https:\/\/github.com\/aferriss\/p5jsShaderExamples\/tree\/gh-pages\/4_image-effects\/4-15_convolution-kernel\">this repo<\/a>. The shader files are already referenced in code we wrote earlier, so we are finished!<\/p>\n<p>The only thing left to do is run your project with <code>npm start<\/code> and take a look at how the filter changes the camera image.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mediadevs\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1651635690\/e-603fc55d218a650069f5228b\/bhunsjhxysqoqmjy92vf.png\" alt=\"camera with shader filter applied\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1418\" height=\"800\"\/><\/p>\n<h2>Finished code<\/h2>\n<p>You can take a look at the complete code in <a href=\"https:\/\/github.com\/flippedcoder\/media-projects\/tree\/main\/camera-filter\">the <code>camera-filter<\/code> folder of this repo<\/a>. Or you can check it out in <a href=\"https:\/\/codesandbox.io\/s\/cocky-flower-25dg3x\">this Code Sandbox<\/a>.<\/p>\n<p>&lt;CodeSandBox\ntitle=\u201ccocky-flower-25dg3x\u201d\nid=\u201ccocky-flower-25dg3x\u201d\n\/&gt;<\/p>\n<h2>Conclusion<\/h2>\n<p>Getting into advanced styling techniques and learning about some of the visualization libraries is a great way to stay ahead of the curve. With all of the virtual interactions we all have, it\u2019s a good skill to know how to render more complex things for users.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28442,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,370,246,371],"class_list":["post-28441","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-image","tag-react","tag-under-review"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Make a Custom Camera Filter in React<\/title>\n<meta name=\"description\" content=\"There are a lot of ways we interact with our cameras these days and using filters is one of them. In this tutorial, we&#039;ll go through the process of adding a custom filter to a camera using the p5.js library.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Make a Custom Camera Filter in React\" \/>\n<meta property=\"og:description\" content=\"There are a lot of ways we interact with our cameras these days and using filters is one of them. In this tutorial, we&#039;ll go through the process of adding a custom filter to a camera using the p5.js library.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-05-13T07:21:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-23T22:50:13+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"6000\" \/>\n\t<meta property=\"og:image:height\" content=\"4000\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Make a Custom Camera Filter in React\",\"datePublished\":\"2022-05-13T07:21:16+00:00\",\"dateModified\":\"2025-02-23T22:50:13+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/\"},\"wordCount\":7,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2.jpg?_i=AA\",\"keywords\":[\"Guest Post\",\"Image\",\"React\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/\",\"name\":\"Make a Custom Camera Filter in React\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2.jpg?_i=AA\",\"datePublished\":\"2022-05-13T07:21:16+00:00\",\"dateModified\":\"2025-02-23T22:50:13+00:00\",\"description\":\"There are a lot of ways we interact with our cameras these days and using filters is one of them. In this tutorial, we'll go through the process of adding a custom filter to a camera using the p5.js library.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2.jpg?_i=AA\",\"width\":6000,\"height\":4000},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Make a Custom Camera Filter in React\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Make a Custom Camera Filter in React","description":"There are a lot of ways we interact with our cameras these days and using filters is one of them. In this tutorial, we'll go through the process of adding a custom filter to a camera using the p5.js library.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/","og_locale":"en_US","og_type":"article","og_title":"Make a Custom Camera Filter in React","og_description":"There are a lot of ways we interact with our cameras these days and using filters is one of them. In this tutorial, we'll go through the process of adding a custom filter to a camera using the p5.js library.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-05-13T07:21:16+00:00","article_modified_time":"2025-02-23T22:50:13+00:00","og_image":[{"width":6000,"height":4000,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2-jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/"},"author":{"name":"","@id":""},"headline":"Make a Custom Camera Filter in React","datePublished":"2022-05-13T07:21:16+00:00","dateModified":"2025-02-23T22:50:13+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/"},"wordCount":7,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2.jpg?_i=AA","keywords":["Guest Post","Image","React","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/","name":"Make a Custom Camera Filter in React","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2.jpg?_i=AA","datePublished":"2022-05-13T07:21:16+00:00","dateModified":"2025-02-23T22:50:13+00:00","description":"There are a lot of ways we interact with our cameras these days and using filters is one of them. In this tutorial, we'll go through the process of adding a custom filter to a camera using the p5.js library.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2.jpg?_i=AA","width":6000,"height":4000},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-a-custom-camera-filter-in-react\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Make a Custom Camera Filter in React"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681928539\/Web_Assets\/blog\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2\/05deb52f3104171f36c9a0353d09964bac9fec57-6000x4000-1_2844244ae2.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28441","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=28441"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28441\/revisions"}],"predecessor-version":[{"id":36982,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28441\/revisions\/36982"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28442"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28441"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28441"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28441"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}