No internet connection
  1. Home
  2. How to

iZotope RX 10 "Selection-manipulation"

By Jack DeCraene @Jack_DeCraene
    2025-10-07 15:21:32.178Z

    Hello!
    Part of my workflow in iZotope RX for cleaning up dialogue is utilizing the lasso tool to select certain imperfections, followed by using the gain module to reduce those imperfections. The problem is, I find that in order to get a nice feathered edge, I have to redraw my selection over and over again with increasingly smaller boundaries, because the inner portions of the selection tend to require greater reduction than the outer portions. Re-drawing over and over again takes a lot of time. Is it possible to script something that would allow me to incrementally grow or shrink a lasso-drawn selection (not just "time-frequency" selections). Thank you!

    • 19 replies

    There are 19 replies. Estimated reading time: 39 minutes

    1. Kitch Membery @Kitch2025-10-07 23:03:03.453Z

      Hi @Jack_DeCraene

      Unfortunately, AFAIK, there is currently no way to programmatically resize/redraw the lasso selection in iZotope RX, other than doing so manually.

      1. JJack DeCraene @Jack_DeCraene
          2025-10-08 00:06:53.141Z

          Ah - I see. Thank you! Would it be possible to do so by utilizing the "time-frequency" too l instead of the lasso?

          1. Dustin Harris @Dustin_Harris
              2025-10-11 15:35:49.551Z

              Hi @Jack_DeCraene
              It is possible in the time+frequency tool.. how many redraws are you thinking, and what sort of increment are you thinking? Here's a bit of a primer that we can adapt with your input. It's not perfect because I've done linear math for the frequency scaling but in reality it should reflect the frequency scale setting (Log, Mel, Bark, etc...)

              
              /* how much you want to shink the selection in percent / 100 */
              const scaleFactor = 0.9;
              
              /* number of selection shrinks */
              const numberOfSelections = 4;
              
              
              /* global constants */
              const rxApp = sf.ui.izotope;
              const editorSelectionPanel = rxApp.mainWindow.groups.whoseDescription.is("App Footer").first.groups.whoseDescription.is("Editor Selection Panel").first
              
              
              /**@typedef {{
               * "Selection start time": string, 
               * "Selection end time": string, 
               * "Selection starting frequency": string, 
               * "Selection ending frequency": string
               * }} RxSelection
              */
              
              
              /**@return RxSelection */
              function getRXSelection() {
              
                  const getPanelElement = (/** @type {string} */ element) =>
                      editorSelectionPanel.buttons.whoseDescription.is(element).first.value.invalidate().value;
                  //end getPanelElement
              
                  /**@type RxSelection */
                  let selection = {
                      "Selection start time": getPanelElement('Selection start time'),
                      "Selection end time": getPanelElement('Selection end time'),
                      "Selection starting frequency": getPanelElement('Selection starting frequency'),
                      "Selection ending frequency": getPanelElement('Selection ending frequency'),
                  };
              
                  return selection;
              }
              
              
              /**
              * @param {RxSelection} selection
              */
              function setRXSelection(selection) {
                  for (let selectionType in selection) {
                      editorSelectionPanel.buttons.whoseDescription.is(selectionType).first.elementSetTextAreaValue({
                          value: selection[selectionType],
                      });
                  }
              }
              
              
              /**
               * @param {RxSelection} rxSelection
               * @param {number} scaleFactor
               * @return RxSelection 
              */
              function transformSelection(rxSelection, scaleFactor) {
              
                  const [startTime, endTime, startFreq, endFreq] = Object.values(rxSelection).map(value => Number(value));
                  
                  const timeRange = endTime - startTime;
                  const freqRange = endFreq - startFreq;
                  const timeCenter = startTime + (timeRange / 2);
                  const freqCenter = startFreq + (freqRange / 2);
                  const newStartTime = Math.round(timeCenter - (timeRange * scaleFactor / 2));
                  const newEndTime = Math.round(timeCenter + (timeRange * scaleFactor / 2));
                  let newStartFreq = freqCenter - (freqRange * scaleFactor / 2);
                  let newEndFreq = freqCenter + (freqRange * scaleFactor / 2);
              
                  /* Adjust precision of frequencies to Rx expected values*/
                  newStartFreq = (newStartFreq < 100) ? Number(newStartFreq.toFixed(2)) : (newStartFreq < 1000) ? Number(newStartFreq.toFixed(1)) : Math.round(newStartFreq);
                  newEndFreq = (newEndFreq < 100) ? Number(newEndFreq.toFixed(2)) : (newEndFreq < 1000) ? Number(newEndFreq.toFixed(1)) : Math.round(newEndFreq);
              
                  /**@type RxSelection */
                  const newSelection = {
                      "Selection start time": String(newStartTime),
                      "Selection end time": String(newEndTime),
                      "Selection starting frequency": String(newStartFreq),
                      "Selection ending frequency": String(newEndFreq),
                  }
              
                  return newSelection;
              
              }
              
              
              
              function getCurrentTimeFormat() { // Get Main Counter so we can change it back when we're done
                  let menuItems = rxApp.getMenuItem('View', 'Time Format').children.first.children.allItems;
                  for (let i = 0; i < menuItems.length; i++) {
                      let currentMenuItem = menuItems[i]
                      if (currentMenuItem.isMenuChecked) {
                          return currentMenuItem.title.value
                      }
                  }
              }
              
              
              
              const setTimeFormatToSamples = () => {
                  rxApp.menuClick({ menuPath: ["View", "Time Format", "Samples"] });
              }
              
              
              const revertTimeFormat = (setting) => {
                  rxApp.menuClick({ menuPath: ["View", "Time Format", setting] });
              }
              
              
              function main() {
                  rxApp.appActivateMainWindow();
                  
                  const originalTimeFormat = getCurrentTimeFormat();
                  
                  setTimeFormatToSamples();
              
                  alert('Process here');
              
                  for (let i = 0; i < numberOfSelections; i++) {
                      const currentSelection = getRXSelection();
                      const newSelection = transformSelection(currentSelection, scaleFactor);
                      setRXSelection(newSelection);
                      alert(`Shrink #${i+1}: Process here`);
                  }
              
                  revertTimeFormat(originalTimeFormat);
              
              }
              
              main();
              
          2. J
            In reply toJack_DeCraene:
            Jack DeCraene @Jack_DeCraene
              2025-10-11 16:21:53.674Z

              Wow - Thank you, Dustin! This is super cool. I just tested it out and I think we've got some strong bones. There are 2 different applications I can think of with this tool that would require slightly different scripts. The first, being what you've done: a proportionally equal shrinkage on all sides. The second, being shrinkage on only the far right-hand side (for use in tapering out on a frequency-based lvl.) For example: only the upper frequencies on harsh "s"s without affecting the lower frequencies.

              For the first script - I think shrinking the selection on a proportional-basis is best because my selections will change in overall size based on scenario. For this, a single click of the command should shrink selection by 15%?

              As for the second tool I think a time-based shrinkage would be best because only the right-hand side of the selection needs to adjust (meaning height can stay the same, and only the width will change - subtracted from the right hand side. This application will almost always be on a sort of micro-level, so maybe each click of the command could shorten the selection by 62.5 milliseconds?

              Apologies for the lengthy explanation - also, if we want to attack one of these at a time, let's do that - Just wanted to express my two different applications. Overall - (as the tool currently works) One click of the command generates several shrinks in succession - however, ideally, only one shrinkage would be great, so that I can toggle between shrink, and gain.

              Thank you!

              1. Dustin Harris @Dustin_Harris
                  2025-10-11 17:39:30.954Z

                  Oh, if you want to trigger a script that just shrinks the selection for you to process as need be, I can simply this a lot, and I'll give you the all sides, and right edge only versions... standby

                  1. Dustin Harris @Dustin_Harris
                      2025-10-11 17:43:42.310Z

                      here you go:

                      Shrink all sides 15%

                      /* how much you want to shink the selection in percent / 100 */
                      const scaleFactor = 0.85;
                      
                      /* global constants */
                      const rxApp = sf.ui.izotope;
                      const editorSelectionPanel = rxApp.mainWindow.groups.whoseDescription.is("App Footer").first.groups.whoseDescription.is("Editor Selection Panel").first
                      
                      
                      /**@typedef {{
                       * "Selection start time": string, 
                       * "Selection end time": string, 
                       * "Selection starting frequency": string, 
                       * "Selection ending frequency": string
                       * }} RxSelection
                      */
                      
                      
                      /**@return RxSelection */
                      function getRXSelection() {
                      
                          const getPanelElement = (/** @type {string} */ element) =>
                              editorSelectionPanel.buttons.whoseDescription.is(element).first.value.invalidate().value;
                          //end getPanelElement
                      
                          /**@type RxSelection */
                          let selection = {
                              "Selection start time": getPanelElement('Selection start time'),
                              "Selection end time": getPanelElement('Selection end time'),
                              "Selection starting frequency": getPanelElement('Selection starting frequency'),
                              "Selection ending frequency": getPanelElement('Selection ending frequency'),
                          };
                      
                          return selection;
                      }
                      
                      
                      /**
                      * @param {RxSelection} selection
                      */
                      function setRXSelection(selection) {
                          for (let selectionType in selection) {
                              editorSelectionPanel.buttons.whoseDescription.is(selectionType).first.elementSetTextAreaValue({
                                  value: selection[selectionType],
                              });
                          }
                      }
                      
                      
                      /**
                       * @param {RxSelection} rxSelection
                       * @param {number} scaleFactor
                       * @return RxSelection 
                      */
                      function transformSelection(rxSelection, scaleFactor) {
                      
                          const [startTime, endTime, startFreq, endFreq] = Object.values(rxSelection).map(value => Number(value));
                      
                          const timeRange = endTime - startTime;
                          const freqRange = endFreq - startFreq;
                          const timeCenter = startTime + (timeRange / 2);
                          const freqCenter = startFreq + (freqRange / 2);
                          const newStartTime = Math.round(timeCenter - (timeRange * scaleFactor / 2));
                          const newEndTime = Math.round(timeCenter + (timeRange * scaleFactor / 2));
                          let newStartFreq = freqCenter - (freqRange * scaleFactor / 2);
                          let newEndFreq = freqCenter + (freqRange * scaleFactor / 2);
                      
                          /* Adjust precision of frequencies to Rx expected values*/
                          newStartFreq = (newStartFreq < 100) ? Number(newStartFreq.toFixed(2)) : (newStartFreq < 1000) ? Number(newStartFreq.toFixed(1)) : Math.round(newStartFreq);
                          newEndFreq = (newEndFreq < 100) ? Number(newEndFreq.toFixed(2)) : (newEndFreq < 1000) ? Number(newEndFreq.toFixed(1)) : Math.round(newEndFreq);
                      
                          /**@type RxSelection */
                          const newSelection = {
                              "Selection start time": String(newStartTime),
                              "Selection end time": String(newEndTime),
                              "Selection starting frequency": String(newStartFreq),
                              "Selection ending frequency": String(newEndFreq),
                          }
                      
                          return newSelection;
                      
                      }
                      
                      
                      
                      function getCurrentTimeFormat() { // Get Main Counter so we can change it back when we're done
                          let menuItems = rxApp.getMenuItem('View', 'Time Format').children.first.children.allItems;
                          for (let i = 0; i < menuItems.length; i++) {
                              let currentMenuItem = menuItems[i]
                              if (currentMenuItem.isMenuChecked) {
                                  return currentMenuItem.title.value
                              }
                          }
                      }
                      
                      
                      
                      const setTimeFormatToSamples = () => {
                          rxApp.menuClick({ menuPath: ["View", "Time Format", "Samples"] });
                      }
                      
                      
                      const revertTimeFormat = (setting) => {
                          rxApp.menuClick({ menuPath: ["View", "Time Format", setting] });
                      }
                      
                      
                      function main() {
                          rxApp.appActivateMainWindow();
                      
                          const originalTimeFormat = getCurrentTimeFormat();
                      
                          setTimeFormatToSamples();
                      
                          const currentSelection = getRXSelection();
                          const newSelection = transformSelection(currentSelection, scaleFactor);
                          setRXSelection(newSelection);
                      
                          revertTimeFormat(originalTimeFormat);
                      
                      }
                      
                      main();
                      

                      and shrink right edge only:

                      
                      /* how much you want to shink the selection in percent / 100 */
                      const scaleFactor = 0.85;
                      
                      /* global constants */
                      const rxApp = sf.ui.izotope;
                      const editorSelectionPanel = rxApp.mainWindow.groups.whoseDescription.is("App Footer").first.groups.whoseDescription.is("Editor Selection Panel").first
                      
                      
                      /**@typedef {{
                       * "Selection start time": string, 
                       * "Selection end time": string, 
                       * "Selection starting frequency": string, 
                       * "Selection ending frequency": string
                       * }} RxSelection
                      */
                      
                      
                      /**@return RxSelection */
                      function getRXSelection() {
                      
                          const getPanelElement = (/** @type {string} */ element) =>
                              editorSelectionPanel.buttons.whoseDescription.is(element).first.value.invalidate().value;
                          //end getPanelElement
                      
                          /**@type RxSelection */
                          let selection = {
                              "Selection start time": getPanelElement('Selection start time'),
                              "Selection end time": getPanelElement('Selection end time'),
                              "Selection starting frequency": getPanelElement('Selection starting frequency'),
                              "Selection ending frequency": getPanelElement('Selection ending frequency'),
                          };
                      
                          return selection;
                      }
                      
                      
                      /**
                      * @param {RxSelection} selection
                      */
                      function setRXSelection(selection) {
                          for (let selectionType in selection) {
                              editorSelectionPanel.buttons.whoseDescription.is(selectionType).first.elementSetTextAreaValue({
                                  value: selection[selectionType],
                              });
                          }
                      }
                      
                      
                      /**
                       * @param {RxSelection} rxSelection
                       * @param {number} scaleFactor
                       * @return RxSelection 
                      */
                      function transformSelection(rxSelection, scaleFactor) {
                      
                          const [startTime, endTime, startFreq, endFreq] = Object.values(rxSelection).map(value => Number(value));
                      
                          const timeRange = endTime - startTime;
                          const freqRange = endFreq - startFreq;
                          const timeCenter = startTime + (timeRange / 2);
                          const freqCenter = startFreq + (freqRange / 2);
                          const newStartTime = Math.round(timeCenter - (timeRange * scaleFactor / 2));
                          const newEndTime = Math.round(timeCenter + (timeRange * scaleFactor / 2));
                          let newStartFreq = freqCenter - (freqRange * scaleFactor / 2);
                          let newEndFreq = freqCenter + (freqRange * scaleFactor / 2);
                      
                          /* Adjust precision of frequencies to Rx expected values*/
                          newStartFreq = (newStartFreq < 100) ? Number(newStartFreq.toFixed(2)) : (newStartFreq < 1000) ? Number(newStartFreq.toFixed(1)) : Math.round(newStartFreq);
                          newEndFreq = (newEndFreq < 100) ? Number(newEndFreq.toFixed(2)) : (newEndFreq < 1000) ? Number(newEndFreq.toFixed(1)) : Math.round(newEndFreq);
                      
                          /**@type RxSelection */
                          const newSelection = {
                              "Selection start time": String(startTime),
                              "Selection end time": String(newEndTime),
                              "Selection starting frequency": String(startFreq),
                              "Selection ending frequency": String(endFreq),
                          }
                      
                          return newSelection;
                      
                      }
                      
                      
                      
                      function getCurrentTimeFormat() { // Get Main Counter so we can change it back when we're done
                          let menuItems = rxApp.getMenuItem('View', 'Time Format').children.first.children.allItems;
                          for (let i = 0; i < menuItems.length; i++) {
                              let currentMenuItem = menuItems[i]
                              if (currentMenuItem.isMenuChecked) {
                                  return currentMenuItem.title.value
                              }
                          }
                      }
                      
                      
                      
                      const setTimeFormatToSamples = () => {
                          rxApp.menuClick({ menuPath: ["View", "Time Format", "Samples"] });
                      }
                      
                      
                      const revertTimeFormat = (setting) => {
                          rxApp.menuClick({ menuPath: ["View", "Time Format", setting] });
                      }
                      
                      
                      function main() {
                          rxApp.appActivateMainWindow();
                      
                          const originalTimeFormat = getCurrentTimeFormat();
                      
                          setTimeFormatToSamples();
                      
                          const currentSelection = getRXSelection();
                          const newSelection = transformSelection(currentSelection, scaleFactor);
                          setRXSelection(newSelection);
                      
                          revertTimeFormat(originalTimeFormat);
                      
                      }
                      
                      main();
                      
                  2. J
                    In reply toJack_DeCraene:
                    Jack DeCraene @Jack_DeCraene
                      2025-10-11 18:24:57.052Z

                      SO WICKED! This is perfect. Thank you so much. Okay - after playing around with the Right-side only shrinker, I'm realizing it would be slick to have the top, left, and bottom individually as well for maximum control. What do I need to do to modify the existing right-side only to make the other sides?

                      1. Dustin Harris @Dustin_Harris
                          2025-10-12 01:50:30.815Z

                          there is this section in the transformSelection function:

                              /**@type RxSelection */
                              const newSelection = {
                                  "Selection start time": String(startTime),
                                  "Selection end time": String(newEndTime),
                                  "Selection starting frequency": String(startFreq),
                                  "Selection ending frequency": String(endFreq),
                              }
                          

                          anything you want to shrink, use the 'new' version (newStartTime, newStartFreq) etc... anything you want to NOT change, use the non-new version (startTime, endFreq), etc :)

                          so, for the start time to stay the same:

                          "Selection start time": String(startTime),
                          

                          and for it to scale:

                          "Selection start time": String(newStartTime),
                          
                          1. JJack DeCraene @Jack_DeCraene
                              2025-10-12 15:05:20.332Z

                              Thank you, Dustin! I really appreciate the help. Stoked to incorporate these into my workflow

                              1. Dustin Harris @Dustin_Harris
                                  2025-10-12 16:20:43.612Z

                                  Just a heads up: I'm going to modify this code (eventually when I have time) to do the frequency scaling properly based on the frequency scale in use in RX... it'll just take time to do all the math :) I'll post it when I'm finished.

                                  1. JJack DeCraene @Jack_DeCraene
                                      2025-10-12 18:45:38.248Z

                                      Oh good catch. I missed that. Will that just effect the accuracy of the frequency proportions? Thanks for the heads up!

                                      1. Dustin Harris @Dustin_Harris
                                          2025-10-12 18:51:49.592Z

                                          Yeah, all things considered it is pretty minor, it works pretty well as-is, at least in my workflow…
                                          It just made my nerd-brain itch a little. I feel like even when the scale isn’t linear, we see/react to the spectrogram linearly anyway, so it might all just be little more than semantics, but I’ll write it and post it; you can decide if it’s better or not :)

                                          1. Dustin Harris @Dustin_Harris
                                              2025-10-12 19:25:17.216Z

                                              Full code with compensation for frequency scales:

                                              
                                              /* how much you want to shink the selection in percent / 100 */
                                              const scaleFactor = 0.85;
                                              
                                              /* global constants */
                                              const rxApp = sf.ui.izotope;
                                              const editorSelectionPanel = rxApp.mainWindow.groups.whoseDescription.is("App Footer").first.groups.whoseDescription.is("Editor Selection Panel").first;
                                              
                                              
                                              /**@typedef {{
                                               * "Selection start time": string, 
                                               * "Selection end time": string, 
                                               * "Selection starting frequency": string, 
                                               * "Selection ending frequency": string
                                               * }} RxSelection
                                              */
                                              
                                              
                                              const setTimeFormatToSamples = () => rxApp.menuClick({ menuPath: ["View", "Time Format", "Samples"] });
                                              const revertTimeFormat = (/**@type string */ setting) => rxApp.menuClick({ menuPath: ["View", "Time Format", setting] });
                                              
                                              
                                              /**@return RxSelection */
                                              function getRXSelection() {
                                              
                                                  const getPanelElement = (/** @type {string} */ element) =>
                                                      editorSelectionPanel.buttons.whoseDescription.is(element).first.value.invalidate().value;
                                                  //end getPanelElement
                                              
                                                  /**@type RxSelection */
                                                  let selection = {
                                                      "Selection start time": getPanelElement('Selection start time'),
                                                      "Selection end time": getPanelElement('Selection end time'),
                                                      "Selection starting frequency": getPanelElement('Selection starting frequency'),
                                                      "Selection ending frequency": getPanelElement('Selection ending frequency'),
                                                  };
                                              
                                                  return selection;
                                              }
                                              
                                              
                                              /**
                                              * @param {RxSelection} selection
                                              */
                                              function setRXSelection(selection) {
                                                  for (let selectionType in selection) {
                                                      editorSelectionPanel.buttons.whoseDescription.is(selectionType).first.elementSetTextAreaValue({
                                                          value: selection[selectionType],
                                                      });
                                                  }
                                              }
                                              
                                              
                                              
                                              /* object containing transformation functions with spectrogram frequency scale setting as key */
                                              const frequencyScales = {
                                              
                                                  /**@param {{lowHz: number, highHz: number, scaleFactor: number}} args  */
                                                  "Linear": function scaleLinear({ lowHz, highHz, scaleFactor }) {
                                              
                                                      const center = (lowHz + highHz) / 2;
                                              
                                                      // Scale distances from center
                                                      const newLowHz = center - (center - lowHz) * scaleFactor;
                                                      const newHighHz = center + (highHz - center) * scaleFactor;
                                              
                                                      return {
                                                          newStartFreq: newLowHz,
                                                          newEndFreq: newHighHz,
                                                      };
                                                  },
                                              
                                                  /**@param {{lowHz: number, highHz: number, scaleFactor: number}} args  */
                                                  "Mel": function scaleMel({ lowHz, highHz, scaleFactor }) {
                                              
                                                      const hzToMel = (/**@type number*/ hz) => 2595 * Math.log10(1 + hz / 700);
                                                      const melToHz = (/**@type number*/ mel) => 700 * (Math.pow(10, mel / 2595) - 1);
                                              
                                                      // Convert to mel scale
                                                      const lowMel = hzToMel(lowHz);
                                                      const highMel = hzToMel(highHz);
                                                      const centerMel = (lowMel + highMel) / 2;
                                              
                                                      // Scale distances from center
                                                      const newLowMel = centerMel - (centerMel - lowMel) * scaleFactor;
                                                      const newHighMel = centerMel + (highMel - centerMel) * scaleFactor;
                                              
                                                      // Convert back to Hz
                                                      return {
                                                          newStartFreq: melToHz(newLowMel),
                                                          newEndFreq: melToHz(newHighMel),
                                                      };
                                                  },
                                              
                                                  /**@param {{lowHz: number, highHz: number, scaleFactor: number}} args  */
                                                  "Bark": function scaleBark({ lowHz, highHz, scaleFactor }) {
                                              
                                                      /**@param {number} hz */
                                                      function hzToBark(hz) {
                                                          // Zwicker & Terhardt approximation - more stable
                                                          return 13 * Math.atan(0.00076 * hz) + 3.5 * Math.atan(Math.pow(hz / 7500, 2));
                                                      }
                                              
                                                      /**@param {number} bark */
                                                      function barkToHz(bark) {
                                                          // Inverse using iterative approximation (Newton-Raphson)
                                                          let hz = 1000; // Initial guess
                                                          const tolerance = 0.001;
                                                          const maxIterations = 100;
                                              
                                                          for (let i = 0; i < maxIterations; i++) {
                                                              const currentBark = hzToBark(hz);
                                                              const error = currentBark - bark;
                                              
                                                              if (Math.abs(error) < tolerance) {
                                                                  return hz;
                                                              }
                                              
                                                              // Numerical derivative
                                                              const delta = 0.1;
                                                              const derivative = (hzToBark(hz + delta) - currentBark) / delta;
                                              
                                                              // Newton-Raphson step
                                                              hz = hz - error / derivative;
                                              
                                                              // Keep hz positive
                                                              if (hz < 0) hz = 0.1;
                                                          }
                                              
                                                          return hz;
                                                      }
                                              
                                              
                                                      // Convert to bark scale
                                                      const lowBark = hzToBark(lowHz);
                                                      const highBark = hzToBark(highHz);
                                                      const centerBark = (lowBark + highBark) / 2;
                                              
                                                      // Scale distances from center
                                                      const newLowBark = centerBark - (centerBark - lowBark) * scaleFactor;
                                                      const newHighBark = centerBark + (highBark - centerBark) * scaleFactor;
                                              
                                                      // Convert back to Hz
                                                      return {
                                                          newStartFreq: barkToHz(newLowBark),
                                                          newEndFreq: barkToHz(newHighBark),
                                                      };
                                                  },
                                              
                                                  /**@param {{lowHz: number, highHz: number, scaleFactor: number}} args  */
                                                  "Log": function scaleLog({ lowHz, highHz, scaleFactor }) {
                                              
                                                      function hzToLog(hz) {
                                                          return Math.log10(hz);
                                                      }
                                              
                                                      function logToHz(logFreq) {
                                                          return Math.pow(10, logFreq);
                                                      }
                                              
                                                      // Convert to log scale
                                                      const lowLog = hzToLog(lowHz);
                                                      const highLog = hzToLog(highHz);
                                                      const centerLog = (lowLog + highLog) / 2;
                                              
                                                      // Scale distances from center
                                                      const newLowLog = centerLog - (centerLog - lowLog) * scaleFactor;
                                                      const newHighLog = centerLog + (highLog - centerLog) * scaleFactor;
                                              
                                                      // Convert back to Hz
                                                      return {
                                                          newStartFreq: logToHz(newLowLog),
                                                          newEndFreq: logToHz(newHighLog),
                                                      };
                                                  },
                                              
                                                  /**@param {{lowHz: number, highHz: number, scaleFactor: number}} args  */
                                                  "Extended Log": function scaleExtendedLog({ lowHz, highHz, scaleFactor }) {
                                              
                                                      const fRef = 1;
                                              
                                                      const hzToExtendedLog = (/**@type number*/ hz) => Math.log10(1 + hz / fRef);
                                                      const extendedLogToHz = (/**@type number*/ extLog) => fRef * (Math.pow(10, extLog) - 1)
                                              
                                                      // Convert to extended log scale
                                                      const lowExtLog = hzToExtendedLog(lowHz);
                                                      const highExtLog = hzToExtendedLog(highHz);
                                                      const centerExtLog = (lowExtLog + highExtLog) / 2;
                                              
                                                      // Scale distances from center
                                                      const newLowExtLog = centerExtLog - (centerExtLog - lowExtLog) * scaleFactor;
                                                      const newHighExtLog = centerExtLog + (highExtLog - centerExtLog) * scaleFactor;
                                              
                                                      // Convert back to Hz
                                                      return {
                                                          newStartFreq: extendedLogToHz(newLowExtLog),
                                                          newEndFreq: extendedLogToHz(newHighExtLog),
                                                      };
                                                  }
                                              }
                                              
                                              
                                              function getFrequencyScale() {
                                              
                                                  const spectrogramSettings = sf.ui.izotope.windows.whoseTitle.is('Spectrogram Settings').first;
                                              
                                                  if (!spectrogramSettings.exists) {
                                              
                                                      sf.ui.izotope.menuClick({
                                                          menuPath: ["View", "Spectrogram Settings"],
                                                      });
                                              
                                                      spectrogramSettings.elementWaitFor();
                                              
                                                  }
                                              
                                                  const currentSpectrogramSetting = spectrogramSettings.popupButtons.whoseTitle.is("Frequency scale").first.value.invalidate().value
                                                  spectrogramSettings.windowClose();
                                                  return currentSpectrogramSetting;
                                              }
                                              
                                              
                                              /**
                                               * @param {RxSelection} rxSelection
                                               * @param {number} scaleFactor
                                               * @return RxSelection 
                                              */
                                              function transformSelection(rxSelection, scaleFactor) {
                                              
                                                  const [startTime, endTime, startFreq, endFreq] = Object.values(rxSelection).map(value => Number(value));
                                              
                                                  const timeRange = endTime - startTime;
                                                  const timeCenter = startTime + (timeRange / 2);
                                                  const newStartTime = Math.round(timeCenter - (timeRange * scaleFactor / 2));
                                                  const newEndTime = Math.round(timeCenter + (timeRange * scaleFactor / 2));
                                              
                                              
                                                  /* get frequency scale from Rx Spectrogram settings window */
                                                  const frequencyScale = getFrequencyScale();
                                                  
                                                  /* get correct tranform function from object using frequency scale as key */
                                                  let frequencyTransform = frequencyScales[frequencyScale];
                                              
                                                  /* scale frequency values */
                                                  let { newStartFreq, newEndFreq } = frequencyTransform({ lowHz: startFreq, highHz: endFreq, scaleFactor });
                                              
                                                  /* Adjust precision of frequencies to Rx expected values*/
                                                  newStartFreq = (newStartFreq < 100) ? Number(newStartFreq.toFixed(2)) : (newStartFreq < 1000) ? Number(newStartFreq.toFixed(1)) : Math.round(newStartFreq);
                                                  newEndFreq = (newEndFreq < 100) ? Number(newEndFreq.toFixed(2)) : (newEndFreq < 1000) ? Number(newEndFreq.toFixed(1)) : Math.round(newEndFreq);
                                              
                                                  /**@type RxSelection */
                                                  const newSelection = {
                                                      "Selection start time": String(newStartTime),
                                                      "Selection end time": String(newEndTime),
                                                      "Selection starting frequency": String(newStartFreq),
                                                      "Selection ending frequency": String(newEndFreq),
                                                  }
                                              
                                                  return newSelection;
                                              }
                                              
                                              
                                              function getCurrentTimeFormat() {
                                                  let menuItems = rxApp.getMenuItem('View', 'Time Format').children.first.children.invalidate().allItems;
                                                  const selectedItem = menuItems.find(item => item.isMenuChecked)
                                                  return selectedItem.title.value;
                                              }
                                              
                                              
                                              
                                              function main() {
                                                  rxApp.appActivateMainWindow();
                                              
                                                  const originalTimeFormat = getCurrentTimeFormat();
                                              
                                                  setTimeFormatToSamples();
                                              
                                                  const currentSelection = getRXSelection();
                                                  const newSelection = transformSelection(currentSelection, scaleFactor);
                                                  setRXSelection(newSelection);
                                              
                                                  revertTimeFormat(originalTimeFormat);
                                              
                                              }
                                              
                                              sf.ui.izotope.appActivateMainWindow();
                                              main();
                                              
                                              1. JJack DeCraene @Jack_DeCraene
                                                  2025-10-13 14:30:48.017Z

                                                  Just gave it a try - I'll be honest... I don't see much of a difference - but now I want to understand the difference! I guess I didn't really understand what you meant. Could you explain what the difference is? The only thing I could tell is that it seems like the first method worked just a little bit faster, but is the accuracy any different?

                                                  1. Dustin Harris @Dustin_Harris
                                                      2025-10-13 16:29:19.703Z

                                                      Yeah it's mostly just semantics to be fair, because no matter what frequency scale we're in, we seem to interpret the 'center' of the selection as visually linear... but in math reality, the centre frequency of the selection is offset by the scale.... not that it matters much for dialogue repair, but think of a frequency selection of two octaves: 100Hz to 400Hz. Octaves are logarithmic, so the center of the octaves (or logarithmic center frequency) is 200Hz... but the linear center frequency is 250hz. Each of the frequency scale types in Rx have a different scaling factor which determines where the center frequency is in the selection, so the upper and lower frequencies are scaled in different amounts relative to linear... if that makes sense. This can be useful though if you're shrinking a selection in between say voice-harmonics, which also are inherently logarithmic... using a logarithmic scale in this instance will ensure the selection shrinks the same distance away from the harmonics above and below the selection. For some applications, that might be important.

                                                      And yeah, it IS slower because it needs to fetch the current frequency scale from the spectrogram settings menu.

                                                      1. JJack DeCraene @Jack_DeCraene
                                                          2025-10-13 18:30:36.273Z

                                                          Ahhhh - Ok, I follow you now. Thanks for the knowledge - that's a good catch. I'll have to keep it in my ack pocket then as I'm sure there'll be a use of that nature! Thanks again for all your help on this, Dustin - great work.

                                                          1. In reply toDustin_Harris:
                                                            JJack DeCraene @Jack_DeCraene
                                                              2025-10-13 20:50:16.253Z

                                                              Oh yeah!! Na - I totally see what you mean now, for scaling larger selections - it adjusts properly for the visual difference between the lower and higher frequencies - worth every bit of that wait time lol. This latest script is definitely the one I'll be using - would you mind sharing the instructions on how to modify it for the single sides? I imagine its a bit more than the old code required. Sorry for all the asks

                                                              1. Dustin Harris @Dustin_Harris
                                                                  2025-10-13 23:10:07.870Z

                                                                  instructions are still the same actually...

                                                                  setting this object like this changes all sides:

                                                                      /**@type RxSelection */
                                                                      const newSelection = {
                                                                          "Selection start time": String(newStartTime),
                                                                          "Selection end time": String(newEndTime),
                                                                          "Selection starting frequency": String(newStartFreq),
                                                                          "Selection ending frequency": String(newEndFreq),
                                                                      }
                                                                  
                                                                      return newSelection;
                                                                  }
                                                                  

                                                                  for things you want to keep the same, just remove the 'new' inside the brackets and make the first letter lower case :)

                                                                  1. JJack DeCraene @Jack_DeCraene
                                                                      2025-10-13 23:53:34.282Z

                                                                      Great!! Thank you