<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Nate</title><description>A seasoned full-stack engineer with 9+ years of experience crafting scalable applications and cloud-native solutions.</description><link>https://nateoneal.com/</link><item><title>Hacky Way to Customize Shadcn’s Tooltip Arrows</title><link>https://nateoneal.com/blog/customize-shadcn-tooltip-arrows/</link><guid isPermaLink="true">https://nateoneal.com/blog/customize-shadcn-tooltip-arrows/</guid><description>A workaround for displaying a custom SVG arrow in shadcn’s tooltip.</description><pubDate>Sun, 15 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ui.shadcn.com/&quot;&gt;Shadcn&lt;/a&gt; is a go-to library for copy-paste UI components in React projects. One commonly used component is the Tooltip, which is built on top of &lt;code&gt;@radix-ui/react-tooltip&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;The default tooltip looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183408/Tooltip_Arrows_Customization_Image_iedgjs.webp&quot; alt=&quot;Shadcn&apos;s tooltip component&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Adding Tooltip Arrows&lt;/h2&gt;
&lt;p&gt;While the provided component works well out of the box, I found myself wanting to add more customization—like adding an arrow to the tooltip. For inspiration, I turned to the Tooltip component from &lt;a href=&quot;https://tremor.so/docs/ui/tooltip&quot;&gt;tremor.so&lt;/a&gt;, which is also based on &lt;code&gt;@radix-ui/react-tooltip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183406/Shadcn_Tooltip_Arrows_Image_o8gzwv.webp&quot; alt=&quot;Tooltip component from tremor.so&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Fortunately, adding an arrow is straightforward since &lt;code&gt;@radix-ui/react-tooltip&lt;/code&gt; includes an &lt;code&gt;Arrow&lt;/code&gt; component. You simply need to include it inside the &lt;code&gt;Content&lt;/code&gt; component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as TooltipPrimitives from &apos;@radix-ui/react-tooltip&apos;

export default () =&amp;gt; (
  &amp;lt;TooltipPrimitives.Provider&amp;gt;
    &amp;lt;TooltipPrimitives.Root&amp;gt;
      &amp;lt;TooltipPrimitives.Trigger /&amp;gt;
      &amp;lt;TooltipPrimitives.Portal&amp;gt;
        &amp;lt;TooltipPrimitives.Content&amp;gt;
          &amp;lt;TooltipPrimitives.Arrow /&amp;gt; // Add the Arrow component here
        &amp;lt;/TooltipPrimitives.Content&amp;gt;
      &amp;lt;/TooltipPrimitives.Portal&amp;gt;
    &amp;lt;/TooltipPrimitives.Root&amp;gt;
  &amp;lt;/TooltipPrimitives.Provider&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;However, what if you want to add a border around the entire tooltip, including the arrow?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Adding a Tooltip Border&lt;/h2&gt;
&lt;p&gt;To achieve this, you’ll need to style the &lt;code&gt;Arrow&lt;/code&gt; component. Let’s explore some approaches:&lt;/p&gt;
&lt;h3&gt;Naive Approach&lt;/h3&gt;
&lt;p&gt;Adding a &lt;code&gt;border&lt;/code&gt; directly to the &lt;code&gt;Arrow&lt;/code&gt; using Tailwind classes seems like a good starting point:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TooltipPrimitives.Arrow
  className=&apos;border border-[var(--tooltip-border-color)] fill-[var(--tooltip-color)]&apos;
  width={12}
  height={7}
  aria-hidden=&apos;true&apos;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this approach doesn’t work as expected. The &lt;code&gt;border&lt;/code&gt; property applies to the rectangular boundary of the element, not the arrow shape itself.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183406/Tooltip_Arrows_Customization_Image_1_mikon1.webp&quot; alt=&quot;Tooltip arrow with square border&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Using the &lt;code&gt;stroke&lt;/code&gt; Property&lt;/h3&gt;
&lt;p&gt;Since the &lt;code&gt;Arrow&lt;/code&gt; is an SVG element, you can use the &lt;code&gt;stroke&lt;/code&gt; property to define a border:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TooltipPrimitives.Arrow
  className=&apos;border-none fill-[var(--tooltip-color)]&apos;
  stroke=&apos;var(--tooltip-border-color)&apos;
  stroke-width=&apos;2&apos;
  width={12}
  height={7}
  aria-hidden=&apos;true&apos;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works better, but the arrow’s top border is still visible. To fix this, let’s explore another method.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183405/Tooltip_Arrows_Customization_u4t1ec.png&quot; alt=&quot;Tooltip arrow with a proper border but visible top border&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Exploring the Drop Shadow Option&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Another method is using a &lt;code&gt;drop-shadow&lt;/code&gt; to simulate the border:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TooltipPrimitives.Arrow
  className=&apos;-my-px border-none fill-[var(--tooltip-color)] drop-shadow-[0_1px_0_red]&apos;
  width={12}
  height={7}
  aria-hidden=&apos;true&apos;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a visually seamless tooltip with an arrow and border, but it might not always be the most precise solution depending on your use case.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183404/Shadcn_Tooltip_Arrows_Image_1_eojqrs.webp&quot; alt=&quot;Tooltip arrow with proper border&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It’s worth mentioning that this solution is inspired by the Tooltip component from &lt;a href=&quot;https://originui.com/&quot;&gt;Origin UI&lt;/a&gt;, which provides various customizable tooltip variants that can save development time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How about styling it even more, like using my own custom SVG?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Custom SVG Arrow&lt;/h2&gt;
&lt;p&gt;I often visit Vercel’s website, and their dropdown navigation bar caught my eye, particularly the &lt;em&gt;arrow&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183409/Tooltip_Arrows_Customization_Image_2_tu7wfm.webp&quot; alt=&quot;Expanded dropdown navigation bar from Vercel&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Curious about how it was designed, I opened the dev tools, inspected the elements, and found the SVG arrow. I then copied it and pasted it into Figma for a closer look.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183407/Tooltip_Arrows_Customization_Image_3_mehrdd.webp&quot; alt=&quot;SVG arrow from Vercel&apos;s dropdown navigation bar&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I thought it could be an interesting design choice for a tooltip arrow.&lt;/p&gt;
&lt;h3&gt;Positioning&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183405/Shadcn_Tooltip_Arrows_Image_2_lwivup.webp&quot; alt=&quot;Tooltip&apos;s position&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Positioning the SVG arrow depends on the tooltip’s placement—&lt;strong&gt;top&lt;/strong&gt;, &lt;strong&gt;bottom&lt;/strong&gt;, &lt;strong&gt;left&lt;/strong&gt;, or &lt;strong&gt;right&lt;/strong&gt;. The &lt;code&gt;Content&lt;/code&gt; component exposes a &lt;code&gt;data-side&lt;/code&gt; attribute, which you can use to adjust positioning dynamically:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[data-side=&apos;top&apos;] svg {
  bottom: -9px;
  left: 50%;
  transform: translateX(-50%);
}

[data-side=&apos;bottom&apos;] svg {
  top: -9px;
  left: 50%;
  transform: translateX(-50%) rotate(180deg);
}

[data-side=&apos;left&apos;] svg {
  right: -20.25px;
  top: 50%;
  transform: translateY(-50%) rotate(-90deg);
}

[data-side=&apos;right&apos;] svg {
  left: -20.25px;
  top: 50%;
  transform: translateY(-50%) rotate(90deg);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we could replace the built-in tooltip &lt;code&gt;Arrow&lt;/code&gt; component with our custom design. It should work perfectly, right?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183408/Tooltip_Arrows_Customization_Image_4_j7v925.webp&quot; alt=&quot;Tooltip with custom SVG arrow&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Well, not quite yet. Take a look at this: the arrow is stuck in the center of the tooltip instead of being positioned near the trigger.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734226077/Screen_Recording_Dec_14_2024_from_Shadcn_Tooltip_Arrows_y2aokg.gif&quot; alt=&quot;Custom SVG arrow centered on the tooltip&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This undesired behavior happens because we only defined a static position for each side. Instead, we need to use a dynamic position to solve this issue.&lt;/p&gt;
&lt;h3&gt;Dynamic Position&lt;/h3&gt;
&lt;p&gt;Let’s start by using the built-in &lt;code&gt;Arrow&lt;/code&gt; component again. If you inspect the &lt;code&gt;Arrow&lt;/code&gt; component in the dev tools while simulating the tooltip position change, you’ll notice that the SVG element is wrapped in a &lt;code&gt;span&lt;/code&gt; with a &lt;code&gt;left&lt;/code&gt; CSS property.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183404/Customize_Shadcn_Tooltip_Arrows_ywmi3y.webp&quot; alt=&quot;Dynamic left CSS value of the built-in tooltip arrow&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This value dynamically changes based on the tooltip&apos;s position. We can capture this &lt;code&gt;left&lt;/code&gt; CSS value and apply it to our custom arrow component.&lt;/p&gt;
&lt;p&gt;To track this &lt;code&gt;left&lt;/code&gt; value, we need to observe it using a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver&quot;&gt;MutationObserver&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// tooltip-arrow-primitive.tsx

import { Arrow } from &apos;@radix-ui/react-tooltip&apos;
import React from &apos;react&apos;

const TooltipArrowPrimitive = () =&amp;gt; {
  const arrowRef = React.useRef&amp;lt;SVGSVGElement&amp;gt;(null)

  React.useEffect(() =&amp;gt; {
    const spanArrow = arrowRef.current?.parentElement

    const observer = new MutationObserver(() =&amp;gt; {
      if (!spanArrow) return

      const style = window.getComputedStyle(spanArrow)

      // Get our custom tooltip component by its id
      const tooltipArrow = document.getElementById(&apos;tooltip-arrow&apos;)

      // Assign the left value as the style attribute
      tooltipArrow?.setAttribute(&apos;style&apos;, `left: ${style.left};`)
    })

    if (spanArrow) {
      observer.observe(spanArrow, {
        attributes: true, // Observe changes to attributes
        attributeFilter: [&apos;style&apos;] // Only look for changes to the style attribute
      })
    }

    return () =&amp;gt; {
      observer.disconnect()
    }
  }, [])

  return &amp;lt;Arrow ref={arrowRef} id=&apos;primitive-arrow&apos; className=&apos;opacity-0&apos; /&amp;gt;
}

export default TooltipArrowPrimitive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we can call it inside the &lt;code&gt;Content&lt;/code&gt; component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import CustomTooltipArrow from &apos;./tooltip-arrow&apos;
import TooltipArrowPrimitive from &apos;./tooltip-arrow-primitive&apos;

export default ({ children, showArrow }) =&amp;gt; (
  // Adjust each component props as needed
  &amp;lt;TooltipPrimitives.Provider&amp;gt;
    &amp;lt;TooltipPrimitives.Root&amp;gt;
      &amp;lt;TooltipPrimitives.Trigger /&amp;gt;
      &amp;lt;TooltipPrimitives.Portal&amp;gt;
        &amp;lt;TooltipPrimitives.Content&amp;gt;
          {children}
          {showArrow &amp;amp;&amp;amp; (
            &amp;lt;&amp;gt;
              &amp;lt;TooltipArrowPrimitive /&amp;gt;
              &amp;lt;CustomTooltipArrow /&amp;gt;
            &amp;lt;/&amp;gt;
          )}
        &amp;lt;/TooltipPrimitives.Content&amp;gt;
      &amp;lt;/TooltipPrimitives.Portal&amp;gt;
    &amp;lt;/TooltipPrimitives.Root&amp;gt;
  &amp;lt;/TooltipPrimitives.Provider&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Final Result&lt;/h3&gt;
&lt;p&gt;It’s working as expected now 🥳🎉&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1734183780/Screen_Recording_Dec_14_from_Shadcn_Tooltip_Arrows_quyz7y.gif&quot; alt=&quot;Final result&quot; /&gt;&lt;/p&gt;
&lt;p&gt;P.S. You might want to adjust the top position if you prefer displaying the tooltip on the left or right side.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;While this approach works, I’m sure there are UI libraries that allow for easier and more flexible tooltip styling. However, it feels rewarding to have found a workaround for this. If you&apos;re interested in exploring other options, you might want to check out &lt;a href=&quot;https://github.com/radix-ui/primitives/discussions/868&quot;&gt;this discussion&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Setting Dynamic Default Values with Zod Schema</title><link>https://nateoneal.com/blog/zod-computed-default-value/</link><guid isPermaLink="true">https://nateoneal.com/blog/zod-computed-default-value/</guid><description>Leverage Zod&apos;s powerful features to set dynamic default values and enhance data validation with flexibility and ease.</description><pubDate>Thu, 12 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Zod provides a powerful way to define schemas for validating and transforming data. You can easily define default values for properties.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;but what if you need a default value based on another property in the schema?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let’s explore how to handle that using Zod.&lt;/p&gt;
&lt;h2&gt;Simple Default Value&lt;/h2&gt;
&lt;p&gt;First, let’s see how we can define a simple default value for a property using Zod:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const schema = z.object({
  name: z.string().default(&apos;John Doe&apos;)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the &lt;strong&gt;name&lt;/strong&gt; property will have a default value of &lt;strong&gt;&apos;John Doe&apos;&lt;/strong&gt; if not supplied during parsing.&lt;/p&gt;
&lt;h2&gt;What If the Default Depends on Another Property?&lt;/h2&gt;
&lt;p&gt;Now, let’s consider a more complex case where the default value of one property depends on another. For instance, imagine we have a blog schema where the &lt;code&gt;displayedTitle&lt;/code&gt; is the same as the title, but with a line break included for formatting purposes.&lt;/p&gt;
&lt;p&gt;Here’s the basic schema:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const blogSchema = z.object({
  title: z.string(),
  displayedTitle: z.string()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let’s define a &lt;code&gt;blogData&lt;/code&gt; object:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const blogData = {
  title: &apos;Bookmarked: Your Go-To Tool for Curating Tweets in Notion&apos;,
  displayedTitle: &apos;Bookmarked: Your Go-To Tool for\nCurating Tweets in Notion&apos;
}

blogSchema.parse(blogData) // parsed successfully and returns blogData
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, both &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;displayedTitle&lt;/code&gt; are provided. But what if we don’t want to define both &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;displayedTitle&lt;/code&gt; manually, especially when they share the same value?&lt;/p&gt;
&lt;h2&gt;Conditional Defaults with &lt;code&gt;transform&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;We can make the &lt;code&gt;displayedTitle&lt;/code&gt; optional and derive it from &lt;code&gt;title&lt;/code&gt; when necessary. This way, you only need to provide one value, and the other one is computed automatically.&lt;/p&gt;
&lt;p&gt;Here’s how to update the schema:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const blogSchema = z
  .object({
    title: z.string(),
    displayedTitle: z.string().optional() // makes displayedTitle optional
  })
  .transform((data) =&amp;gt; ({
    ...data,
    displayedTitle: data.displayedTitle || data.title // fallback to title if displayedTitle is undefined
  }))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if &lt;code&gt;displayedTitle&lt;/code&gt; is not provided, it will fall back to using the title property.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const blogData = {
  title: &apos;This is the title without linebreak&apos;
}

const parsedData = blogSchema.parse(blogData) // parsed successfully
console.log(parsedData)
// {
//   title: &quot;This is the title without linebreak&quot;,
//   displayedTitle: &quot;This is the title without linebreak&quot;
// }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, displayedTitle is derived from title automatically when it&apos;s missing.&lt;/p&gt;
&lt;h2&gt;Simplifying the Schema with Inferred Values&lt;/h2&gt;
&lt;p&gt;We can further simplify this by only defining the &lt;code&gt;displayedTitle&lt;/code&gt;, and having title inferred automatically by transforming the value. For instance, you might want to replace line breaks (\n) in &lt;code&gt;displayedTitle&lt;/code&gt; with spaces in title:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const blogSchema = z
  .object({
    displayedTitle: z.string()
  })
  .transform((data) =&amp;gt; ({
    ...data,
    title: data.displayedTitle.replace(&apos;\\n&apos;, &apos; &apos;) // removes the linebreaks from title
  }))

const blogData = {
  displayedTitle: &apos;This is the title\nwith linebreak&apos;
}

const parsedData = blogSchema.parse(blogData) // parsed successfully
console.log(parsedData)
// {
//   title: &quot;This is the title with linebreak&quot;,
//   displayedTitle: &quot;This is the title\nwith linebreak&quot;
// }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we’ve reduced the schema to only require displayedTitle, and the title is inferred automatically by removing the \n characters.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Zod allows you to create flexible schemas that validate data and derive properties based on other values. Its transform method offers an elegant solution for handling default values and derived properties efficiently.&lt;/p&gt;
</content:encoded></item><item><title>Level Up OBS Recordings with Zoom-to-Mouse</title><link>https://nateoneal.com/blog/zoom-to-mouse-functionality-in-obs/</link><guid isPermaLink="true">https://nateoneal.com/blog/zoom-to-mouse-functionality-in-obs/</guid><description>Enhance your recordings and presentations with dynamic zoom effects.</description><pubDate>Fri, 03 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import Video from &apos;@/components/Video&apos;&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Want to make your recordings more dynamic and engaging? Check out the video below to see the zoom-to-mouse effect in action&lt;/p&gt;
&lt;p&gt;&amp;lt;Video
src=&apos;https://res.cloudinary.com/dj1jdtddj/video/upload/v1735908341/Video_ekj6op.mp4&apos;
poster=&apos;https://res.cloudinary.com/dj1jdtddj/image/upload/v1735906741/4ea8c3d8-7cf9-4c03-9d9e-bb39627a94e3.png&apos;
client:only=&apos;react&apos;
/&amp;gt;&lt;/p&gt;
&lt;p&gt;While paid tools like &lt;a href=&quot;https://screen.studio/&quot;&gt;Screen Studio&lt;/a&gt; offer this capability, what if you could achieve the same effect for free? The good news is that you can, using OBS Studio!&lt;/p&gt;
&lt;p&gt;In this guide, I&apos;ll walk you through the steps to set up zoom-to-mouse functionality. These instructions have been tested on Windows and Mac, and the process should be quite similar for Linux users.&lt;/p&gt;
&lt;h2&gt;1. Download the Magical Script&lt;/h2&gt;
&lt;p&gt;To get started, you’ll need to download the script that enables the zoom-to-mouse functionality in OBS Studio&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open this &lt;a href=&quot;https://github.com/BlankSourceCode/obs-zoom-to-mouse/releases/tag/v1.0.1&quot;&gt;link&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In the &lt;em&gt;Assets&lt;/em&gt; section, click to download the &lt;code&gt;obs-zoom-to-mouse.lua&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1735877527/1_yqel5r.png&quot; alt=&quot;Download the zoom-to-mouse script from github&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;2. Set Up Your OBS Screen&lt;/h2&gt;
&lt;p&gt;Now that you have the script, let’s set up your OBS scene:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Launch OBS.&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;New Scene&lt;/strong&gt; by clicking the &lt;strong&gt;+&lt;/strong&gt; button under the &lt;em&gt;Scenes&lt;/em&gt; section.&lt;/li&gt;
&lt;li&gt;Next, add a &lt;strong&gt;New Source&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;On &lt;strong&gt;Mac&lt;/strong&gt;, select &lt;code&gt;macOS Screen Capture&lt;/code&gt; as the source.&lt;/li&gt;
&lt;li&gt;On &lt;strong&gt;Windows&lt;/strong&gt;, choose &lt;code&gt;Display Capture&lt;/code&gt; as the source.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Important: Not all sources work with this script, so ensure you pick the right one.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1735877526/2_dfh1kg.webp&quot; alt=&quot;OBS screen setup&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;3. Setup the zoom to mouse script&lt;/h2&gt;
&lt;p&gt;Now let’s connect the script to your scene:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Tools&lt;/strong&gt; in the top menu bar.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Scripts&lt;/strong&gt; from the dropdown.&lt;/li&gt;
&lt;li&gt;In the &lt;em&gt;Scripts&lt;/em&gt; tab, click the plus icon (+) to add the &lt;code&gt;obs-zoom-to-mouse.lua&lt;/code&gt; script.&lt;/li&gt;
&lt;li&gt;On the right side, you’ll see configuration options. Choose the &lt;strong&gt;Zoom Source&lt;/strong&gt; you created earlier (if you can’t find it, click &lt;em&gt;Refresh zoom sources&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can adjust the configuration to your liking, but to match the result from my video, here’s the setup:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1735877527/3_karcfa.webp&quot; alt=&quot;Zoom to mouse script configuration&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tip for Mac Retina Displays&lt;/strong&gt;: If you&apos;re on a Mac with a retina display, you might experience some weird behavior with zoom-to-mouse. To fix this, check the option for &lt;strong&gt;Set Manual Source Position&lt;/strong&gt; and refer to the &lt;a href=&quot;https://github.com/BlankSourceCode/obs-zoom-to-mouse/issues/24&quot;&gt;issue discussion&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;For a smoother experience on Retina displays, you can adjust the &lt;code&gt;Scale X&lt;/code&gt; and &lt;code&gt;Scale Y&lt;/code&gt; to &lt;code&gt;2&lt;/code&gt;, and make sure to set the &lt;code&gt;Monitor Width&lt;/code&gt; and &lt;code&gt;Monitor Height&lt;/code&gt; based on your screen resolution. Here&apos;s my configuration for reference:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1735877527/4_lpiocl.webp&quot; alt=&quot;Additional configuration for Mac Retina Dipslay&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;4. Setup OBS Hotkey&lt;/h2&gt;
&lt;p&gt;To control the zoom-to-mouse functionality, you’ll need to configure some hotkeys:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings&lt;/strong&gt; in OBS.&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;Hotkeys&lt;/strong&gt; section in the left sidebar.&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;Toggle zoom to mouse&lt;/strong&gt; and &lt;strong&gt;Toggle follow mouse during zoom&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Assign your preferred hotkeys (I recommend &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;2&lt;/code&gt; for easy access).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://res.cloudinary.com/dj1jdtddj/image/upload/v1735877527/5_vzyepk.png&quot; alt=&quot;Hotkey setup&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Setup Global Hotkeys (Mac Only)&lt;/h3&gt;
&lt;p&gt;For now the hotkeys are only active when OBS is in focus. If you want to use the hotkeys while working in other applications, you need to set up global hotkeys:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Enable Accessibility Permissions&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open &lt;strong&gt;System Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Privacy &amp;amp; Security&lt;/strong&gt; in the sidebar.&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Accessibility&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add OBS to the list of apps that can control your computer.&lt;/li&gt;
&lt;li&gt;Toggle the switch to enable the app.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Allow Input Monitoring&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Back in the &lt;strong&gt;Privacy &amp;amp; Security&lt;/strong&gt; tab, select &lt;strong&gt;Input Monitoring&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add OBS to the list of apps that can monitor input.&lt;/li&gt;
&lt;li&gt;Toggle the switch to enable the app.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Please note&lt;/strong&gt;: There is a known issue where this configuration may only work for a few hours. If you encounter this, try restarting your computer to restore functionality. You can read more about this issue &lt;a href=&quot;https://github.com/obsproject/obs-studio/issues/4126#issuecomment-1960956749&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;And That’s It!&lt;/h2&gt;
&lt;p&gt;Now you&apos;re ready to use the zoom-to-mouse functionality in OBS Studio. Simply press &apos;1&apos; to zoom in and out, and &apos;2&apos; to toggle the zoom following your mouse. This feature can help make your tutorials, presentations, or streams more engaging.&lt;/p&gt;
</content:encoded></item></channel></rss>