[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M79404F3WBQFTQWNJ0YNTPTC":3,"skill-same:01M79404F3WBQFTQWNJ0YNTPTC":44},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":18,"linkOnly":19,"markdownContent":20,"markdownBody":21,"frontMatter":22,"refName":23,"commitSha":24,"skillDirectory":25,"forksCount":13,"repositoryHtmlUrl":26,"repositoryHomepageUrl":27,"ownerHtmlUrl":28,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":29,"install":30,"resources":43},"01M79404F3WBQFTQWNJ0YNTPTC","gsap-utils",null,"Official GSAP skill for gsap.utils — clamp, mapRange, normalize, interpolate, random, snap, toArray, wrap, pipe. Use when the user asks about gsap.utils, clamp, mapRange, random, snap, toArray, wrap, or helper utilities in GSAP.","stickmanbenn-byte\u002Fgrumbledemo","stickmanbenn-byte","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F302456393?v=4","01M78GVWRBTVVYH7SRZRKAFFHH","01M793E99J2CR1KM0GJ3BXR6VX",0,"MIT","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgsap-utils\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: gsap-utils\ndescription: Official GSAP skill for gsap.utils — clamp, mapRange, normalize, interpolate, random, snap, toArray, wrap, pipe. Use when the user asks about gsap.utils, clamp, mapRange, random, snap, toArray, wrap, or helper utilities in GSAP.\nlicense: MIT\n---\n\n# gsap.utils\n\n## When to Use This Skill\n\nApply when writing or reviewing code that uses **gsap.utils** for math, array\u002Fcollection handling, unit parsing, or value mapping in animations (e.g. mapping scroll to a value, randomizing, snapping to a grid, or normalizing inputs).\n\n**Related skills:** Use with **gsap-core**, **gsap-timeline**, and **gsap-scrolltrigger** when building animations; CustomEase and other easing utilities are in **gsap-plugins**.\n\n## Overview\n\n**gsap.utils** provides pure helpers; no need to register. Use in tween vars (e.g. function-based values), in ScrollTrigger or Observer callbacks, or in any JS that drives GSAP. All are on **gsap.utils** (e.g. `gsap.utils.clamp()`).\n\n**Omitting the value: function form.** Many utils accept the value to transform as the **last** argument. If you omit that argument, the util returns a **function** that accepts the value later. Use the function form when you need to clamp, map, normalize, or snap many values with the same config (e.g. in a mousemove handler or tween callback). **Exception: random()** — pass **true** as the last argument to get a reusable function (do not omit the value); see [random()](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FGSAP\u002FUtilityMethods\u002Frandom()).\n\n```javascript\n\u002F\u002F With value: returns the result\ngsap.utils.clamp(0, 100, 150); \u002F\u002F 100\n\n\u002F\u002F Without value: returns a function you call with the value later\nlet c = gsap.utils.clamp(0, 100);\nc(150);  \u002F\u002F 100\nc(-10);  \u002F\u002F 0\n```\n\n## Clamping and Ranges\n\n### clamp(min, max, value?)\n\nConstrains a value between min and max. Omit **value** to get a function: `clamp(min, max)(value)`.\n\n```javascript\ngsap.utils.clamp(0, 100, 150); \u002F\u002F 100\ngsap.utils.clamp(0, 100, -10); \u002F\u002F 0\n\nlet clampFn = gsap.utils.clamp(0, 100);\nclampFn(150); \u002F\u002F 100\n```\n\n### mapRange(inMin, inMax, outMin, outMax, value?)\n\nMaps a value from one range to another. Use when converting scroll position, progress (0–1), or input range to an animation range. Omit **value** to get a function: `mapRange(inMin, inMax, outMin, outMax)(value)`.\n\n```javascript\ngsap.utils.mapRange(0, 100, 0, 500, 50);  \u002F\u002F 250\ngsap.utils.mapRange(0, 1, 0, 360, 0.5);   \u002F\u002F 180 (progress to degrees)\n\nlet mapFn = gsap.utils.mapRange(0, 100, 0, 500);\nmapFn(50);  \u002F\u002F 250\n```\n\n### normalize(min, max, value?)\n\nReturns a value normalized to 0–1 for the given range. Inverse of mapping when the target range is 0–1. Omit **value** to get a function: `normalize(min, max)(value)`.\n\n```javascript\ngsap.utils.normalize(0, 100, 50);   \u002F\u002F 0.5\ngsap.utils.normalize(100, 300, 200); \u002F\u002F 0.5\n\nlet normFn = gsap.utils.normalize(0, 100);\nnormFn(50); \u002F\u002F 0.5\n```\n\n### interpolate(start, end, progress?)\n\nInterpolates between two values at a given progress (0–1). Handles numbers, colors, and objects with matching keys. Omit **progress** to get a function: `interpolate(start, end)(progress)`.\n\n```javascript\ngsap.utils.interpolate(0, 100, 0.5);       \u002F\u002F 50\ngsap.utils.interpolate(\"#ff0000\", \"#0000ff\", 0.5); \u002F\u002F mid color\ngsap.utils.interpolate({ x: 0, y: 0 }, { x: 100, y: 50 }, 0.5); \u002F\u002F { x: 50, y: 25 }\n\nlet lerp = gsap.utils.interpolate(0, 100);\nlerp(0.5); \u002F\u002F 50\n```\n\n## Random and Snap\n\n### random(minimum, maximum[, snapIncrement, returnFunction]) \u002F random(array[, returnFunction])\n\nReturns a random number in the range **minimum**–**maximum**, or a random element from an **array**. Optional **snapIncrement** snaps the result to the nearest multiple (e.g. `5` → multiples of 5). **To get a reusable function**, pass **true** as the last argument (**returnFunction**); the returned function takes no args and returns a new random value each time. This is the only util that uses `true` for the function form instead of omitting the value.\n\n```javascript\n\u002F\u002F immediate value: number in range\ngsap.utils.random(-100, 100);        \u002F\u002F e.g. 42.7\ngsap.utils.random(0, 500, 5);        \u002F\u002F 0–500, snapped to nearest 5\n\n\u002F\u002F reusable function: pass true as last argument\nlet randomFn = gsap.utils.random(-200, 500, 10, true);\nrandomFn();  \u002F\u002F random value in range, snapped to 10\nrandomFn();  \u002F\u002F another random value\n\n\u002F\u002F array: pick one value at random\ngsap.utils.random([\"red\", \"blue\", \"green\"]);  \u002F\u002F \"red\", \"blue\", or \"green\"\nlet randomFromArray = gsap.utils.random([0, 100, 200], true);\nrandomFromArray();  \u002F\u002F 0, 100, or 200\n```\n\n**String form in tween vars:** use `\"random(-100, 100)\"`, `\"random(-100, 100, 5)\"`, or `\"random([0, 100, 200])\"`; GSAP evaluates it per target.\n\n```javascript\ngsap.to(\".box\", { x: \"random(-100, 100, 5)\", duration: 1 });\ngsap.to(\".item\", { backgroundColor: \"random([red, blue, green])\" });\n```\n\n### snap(snapTo, value?)\n\nSnaps a value to the nearest multiple of **snapTo**, or to the nearest value in an array of allowed values. Omit **value** to get a function: `snap(snapTo)(value)` (or `snap(snapArray)(value)`).\n\n```javascript\ngsap.utils.snap(10, 23);     \u002F\u002F 20\ngsap.utils.snap(0.25, 0.7);  \u002F\u002F 0.75\ngsap.utils.snap([0, 100, 200], 150); \u002F\u002F 100 or 200 (nearest in array)\n\nlet snapFn = gsap.utils.snap(10);\nsnapFn(23); \u002F\u002F 20\n```\n\nUse in tweens for grid or step-based animation:\n\n```javascript\ngsap.to(\".x\", { x: 200, snap: { x: 20 } });\n```\n\n### shuffle(array)\n\nReturns a new array with the same elements in random order. Use for randomizing order (e.g. stagger from \"random\" with a copy).\n\n```javascript\ngsap.utils.shuffle([1, 2, 3, 4]); \u002F\u002F e.g. [3, 1, 4, 2]\n```\n\n### distribute(config)\n\n**Returns a function** that assigns a value to each target based on its position in the array (or in a grid). Used internally for advanced staggers; use it whenever you need values spread across many elements (e.g. scale, opacity, x, delay). The returned function receives `(index, target, targets)` — either call it manually or pass the result directly into a tween; GSAP will call it per target with index, element, and array.\n\n**Config (all optional):**\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `base` | Number | Starting value. Default `0`. |\n| `amount` | Number | Total to distribute across all targets (added to base). E.g. `amount: 1` with 100 targets → 0.01 between each. Use **each** instead to set a fixed step per target. |\n| `each` | Number | Amount to add between each target (added to base). E.g. `each: 1` with 4 targets → 0, 1, 2, 3. Use **amount** instead to split a total. |\n| `from` | Number \\| String \\| Array | Where distribution starts: index, or `\"start\"`, `\"center\"`, `\"edges\"`, `\"random\"`, `\"end\"`, or ratios like `[0.25, 0.75]`. Default `0`. |\n| `grid` | String \\| Array | Use grid position instead of flat index: `[rows, columns]` (e.g. `[5, 10]`) or `\"auto\"` to detect. Omit for flat array. |\n| `axis` | String | For grid: limit to one axis (`\"x\"` or `\"y\"`). |\n| `ease` | Ease | Distribute values along an ease curve (e.g. `\"power1.inOut\"`). Default `\"none\"`. |\n\n**In a tween:** pass the result of `distribute(config)` as the property value; GSAP calls the function for each target with `(index, target, targets)`.\n\n```javascript\n\u002F\u002F Scale: middle elements 0.5, outer edges 3 (amount 2.5 distributed from center)\ngsap.to(\".class\", {\n  scale: gsap.utils.distribute({\n    base: 0.5,\n    amount: 2.5,\n    from: \"center\"\n  })\n});\n```\n\n**Manual use:** call the returned function with `(index, target, targets)` to get the value for that index.\n\n```javascript\nconst distributor = gsap.utils.distribute({\n  base: 50,\n  amount: 100,\n  from: \"center\",\n  ease: \"power1.inOut\"\n});\nconst targets = gsap.utils.toArray(\".box\");\nconst valueForIndex2 = distributor(2, targets[2], targets);\n```\n\nSee [distribute()](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FGSAP\u002FUtilityMethods\u002Fdistribute\u002F) for more.\n\n## Units and Parsing\n\n### getUnit(value)\n\nReturns the unit string of a value (e.g. `\"px\"`, `\"%\"`, `\"deg\"`). Use when normalizing or converting values.\n\n```javascript\ngsap.utils.getUnit(\"100px\");   \u002F\u002F \"px\"\ngsap.utils.getUnit(\"50%\");     \u002F\u002F \"%\"\ngsap.utils.getUnit(42);        \u002F\u002F \"\" (unitless)\n```\n\n### unitize(value, unit)\n\nAppends a unit to a number, or returns the value as-is if it already has a unit. Use when building CSS values or tween end values.\n\n```javascript\ngsap.utils.unitize(100, \"px\");  \u002F\u002F \"100px\"\ngsap.utils.unitize(\"2rem\", \"px\"); \u002F\u002F \"2rem\" (unchanged)\n```\n\n### splitColor(color, returnHSL?)\n\nConverts a color string into an array: **[red, green, blue]** (0–255), or **[red, green, blue, alpha]** (4 elements for RGBA when alpha is present or required). Pass **true** as the second argument (**returnHSL**) to get **[hue, saturation, lightness]** or **[hue, saturation, lightness, alpha]** (HSL\u002FHSLA) instead. Works with `\"rgb()\"`, `\"rgba()\"`, `\"hsl()\"`, `\"hsla()\"`, hex, and named colors (e.g. `\"red\"`). Use when animating color components or building gradients. See [splitColor()](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FGSAP\u002FUtilityMethods\u002FsplitColor\u002F).\n\n```javascript\ngsap.utils.splitColor(\"red\");                    \u002F\u002F [255, 0, 0]\ngsap.utils.splitColor(\"#6fb936\");                \u002F\u002F [111, 185, 54]\ngsap.utils.splitColor(\"rgba(204, 153, 51, 0.5)\"); \u002F\u002F [204, 153, 51, 0.5] (4 elements)\ngsap.utils.splitColor(\"#6fb936\", true);          \u002F\u002F [94, 55, 47] (HSL: hue, saturation, lightness)\n```\n\n## Arrays and Collections\n\n### selector(scope)\n\nReturns a scoped selector function that finds elements only within the given element (or ref). Use in components so selectors like `\".box\"` match only descendants of that component, not the whole document. Accepts a DOM element or a ref (e.g. React ref; handles `.current`).\n\n```javascript\nconst q = gsap.utils.selector(containerRef);\nq(\".box\");        \u002F\u002F array of .box elements inside container\ngsap.to(q(\".circle\"), { x: 100 });\n```\n\n### toArray(value, scope?)\n\nConverts a value to an array: selector string (scoped to element), NodeList, HTMLCollection, single element, or array. Use when passing mixed inputs to GSAP (e.g. targets) and a true array is needed.\n\n```javascript\ngsap.utils.toArray(\".item\");           \u002F\u002F array of elements\ngsap.utils.toArray(\".item\", container); \u002F\u002F scoped to container\ngsap.utils.toArray(nodeList);          \u002F\u002F [ ... ] from NodeList\n```\n\n### pipe(...functions)\n\nComposes functions: **pipe(f1, f2, f3)(value)** returns f3(f2(f1(value))). Use when applying a chain of transforms (e.g. normalize → mapRange → snap) in a tween or callback.\n\n```javascript\nconst fn = gsap.utils.pipe(\n  (v) => gsap.utils.normalize(0, 100, v),\n  (v) => gsap.utils.snap(0.1, v)\n);\nfn(50); \u002F\u002F normalized then snapped\n```\n\n### wrap(min, max, value?)\n\nWraps a value into the range min–max (inclusive min, exclusive max). Use for infinite scroll or cyclic values. Omit **value** to get a function: `wrap(min, max)(value)`.\n\n```javascript\ngsap.utils.wrap(0, 360, 370);  \u002F\u002F 10\ngsap.utils.wrap(0, 360, -10);   \u002F\u002F 350\n\nlet wrapFn = gsap.utils.wrap(0, 360);\nwrapFn(370); \u002F\u002F 10\n```\n\n### wrapYoyo(min, max, value?)\n\nWraps value in range with a yoyo (bounces at ends). Use for back-and-forth within a range. Omit **value** to get a function: `wrapYoyo(min, max)(value)`.\n\n```javascript\ngsap.utils.wrapYoyo(0, 100, 150); \u002F\u002F 50 (bounces back)\n\nlet wrapY = gsap.utils.wrapYoyo(0, 100);\nwrapY(150); \u002F\u002F 50\n```\n\n## Best practices\n\n- ✅ Omit the value argument to get a reusable function when the same range\u002Fconfig is used many times (e.g. scroll handler, tween callback): `let mapFn = gsap.utils.mapRange(0, 1, 0, 360); mapFn(progress)`.\n- ✅ Use **snap** for grid-aligned or step-based values; use **toArray** when GSAP or your code needs a real array from a selector or NodeList.\n- ✅ Use **gsap.utils.selector(scope)** in components so selectors are scoped to a container or ref.\n\n## Do Not\n\n- ❌ Assume **mapRange** \u002F **normalize** handle units; they work on numbers. Use **getUnit** \u002F **unitize** when units matter.\n- ❌ Override or rely on undocumented behavior; stick to the documented API.\n\n### Learn More\n\nhttps:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FHelperFunctions\n","# gsap.utils\n\n## When to Use This Skill\n\nApply when writing or reviewing code that uses **gsap.utils** for math, array\u002Fcollection handling, unit parsing, or value mapping in animations (e.g. mapping scroll to a value, randomizing, snapping to a grid, or normalizing inputs).\n\n**Related skills:** Use with **gsap-core**, **gsap-timeline**, and **gsap-scrolltrigger** when building animations; CustomEase and other easing utilities are in **gsap-plugins**.\n\n## Overview\n\n**gsap.utils** provides pure helpers; no need to register. Use in tween vars (e.g. function-based values), in ScrollTrigger or Observer callbacks, or in any JS that drives GSAP. All are on **gsap.utils** (e.g. `gsap.utils.clamp()`).\n\n**Omitting the value: function form.** Many utils accept the value to transform as the **last** argument. If you omit that argument, the util returns a **function** that accepts the value later. Use the function form when you need to clamp, map, normalize, or snap many values with the same config (e.g. in a mousemove handler or tween callback). **Exception: random()** — pass **true** as the last argument to get a reusable function (do not omit the value); see [random()](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FGSAP\u002FUtilityMethods\u002Frandom()).\n\n```javascript\n\u002F\u002F With value: returns the result\ngsap.utils.clamp(0, 100, 150); \u002F\u002F 100\n\n\u002F\u002F Without value: returns a function you call with the value later\nlet c = gsap.utils.clamp(0, 100);\nc(150);  \u002F\u002F 100\nc(-10);  \u002F\u002F 0\n```\n\n## Clamping and Ranges\n\n### clamp(min, max, value?)\n\nConstrains a value between min and max. Omit **value** to get a function: `clamp(min, max)(value)`.\n\n```javascript\ngsap.utils.clamp(0, 100, 150); \u002F\u002F 100\ngsap.utils.clamp(0, 100, -10); \u002F\u002F 0\n\nlet clampFn = gsap.utils.clamp(0, 100);\nclampFn(150); \u002F\u002F 100\n```\n\n### mapRange(inMin, inMax, outMin, outMax, value?)\n\nMaps a value from one range to another. Use when converting scroll position, progress (0–1), or input range to an animation range. Omit **value** to get a function: `mapRange(inMin, inMax, outMin, outMax)(value)`.\n\n```javascript\ngsap.utils.mapRange(0, 100, 0, 500, 50);  \u002F\u002F 250\ngsap.utils.mapRange(0, 1, 0, 360, 0.5);   \u002F\u002F 180 (progress to degrees)\n\nlet mapFn = gsap.utils.mapRange(0, 100, 0, 500);\nmapFn(50);  \u002F\u002F 250\n```\n\n### normalize(min, max, value?)\n\nReturns a value normalized to 0–1 for the given range. Inverse of mapping when the target range is 0–1. Omit **value** to get a function: `normalize(min, max)(value)`.\n\n```javascript\ngsap.utils.normalize(0, 100, 50);   \u002F\u002F 0.5\ngsap.utils.normalize(100, 300, 200); \u002F\u002F 0.5\n\nlet normFn = gsap.utils.normalize(0, 100);\nnormFn(50); \u002F\u002F 0.5\n```\n\n### interpolate(start, end, progress?)\n\nInterpolates between two values at a given progress (0–1). Handles numbers, colors, and objects with matching keys. Omit **progress** to get a function: `interpolate(start, end)(progress)`.\n\n```javascript\ngsap.utils.interpolate(0, 100, 0.5);       \u002F\u002F 50\ngsap.utils.interpolate(\"#ff0000\", \"#0000ff\", 0.5); \u002F\u002F mid color\ngsap.utils.interpolate({ x: 0, y: 0 }, { x: 100, y: 50 }, 0.5); \u002F\u002F { x: 50, y: 25 }\n\nlet lerp = gsap.utils.interpolate(0, 100);\nlerp(0.5); \u002F\u002F 50\n```\n\n## Random and Snap\n\n### random(minimum, maximum[, snapIncrement, returnFunction]) \u002F random(array[, returnFunction])\n\nReturns a random number in the range **minimum**–**maximum**, or a random element from an **array**. Optional **snapIncrement** snaps the result to the nearest multiple (e.g. `5` → multiples of 5). **To get a reusable function**, pass **true** as the last argument (**returnFunction**); the returned function takes no args and returns a new random value each time. This is the only util that uses `true` for the function form instead of omitting the value.\n\n```javascript\n\u002F\u002F immediate value: number in range\ngsap.utils.random(-100, 100);        \u002F\u002F e.g. 42.7\ngsap.utils.random(0, 500, 5);        \u002F\u002F 0–500, snapped to nearest 5\n\n\u002F\u002F reusable function: pass true as last argument\nlet randomFn = gsap.utils.random(-200, 500, 10, true);\nrandomFn();  \u002F\u002F random value in range, snapped to 10\nrandomFn();  \u002F\u002F another random value\n\n\u002F\u002F array: pick one value at random\ngsap.utils.random([\"red\", \"blue\", \"green\"]);  \u002F\u002F \"red\", \"blue\", or \"green\"\nlet randomFromArray = gsap.utils.random([0, 100, 200], true);\nrandomFromArray();  \u002F\u002F 0, 100, or 200\n```\n\n**String form in tween vars:** use `\"random(-100, 100)\"`, `\"random(-100, 100, 5)\"`, or `\"random([0, 100, 200])\"`; GSAP evaluates it per target.\n\n```javascript\ngsap.to(\".box\", { x: \"random(-100, 100, 5)\", duration: 1 });\ngsap.to(\".item\", { backgroundColor: \"random([red, blue, green])\" });\n```\n\n### snap(snapTo, value?)\n\nSnaps a value to the nearest multiple of **snapTo**, or to the nearest value in an array of allowed values. Omit **value** to get a function: `snap(snapTo)(value)` (or `snap(snapArray)(value)`).\n\n```javascript\ngsap.utils.snap(10, 23);     \u002F\u002F 20\ngsap.utils.snap(0.25, 0.7);  \u002F\u002F 0.75\ngsap.utils.snap([0, 100, 200], 150); \u002F\u002F 100 or 200 (nearest in array)\n\nlet snapFn = gsap.utils.snap(10);\nsnapFn(23); \u002F\u002F 20\n```\n\nUse in tweens for grid or step-based animation:\n\n```javascript\ngsap.to(\".x\", { x: 200, snap: { x: 20 } });\n```\n\n### shuffle(array)\n\nReturns a new array with the same elements in random order. Use for randomizing order (e.g. stagger from \"random\" with a copy).\n\n```javascript\ngsap.utils.shuffle([1, 2, 3, 4]); \u002F\u002F e.g. [3, 1, 4, 2]\n```\n\n### distribute(config)\n\n**Returns a function** that assigns a value to each target based on its position in the array (or in a grid). Used internally for advanced staggers; use it whenever you need values spread across many elements (e.g. scale, opacity, x, delay). The returned function receives `(index, target, targets)` — either call it manually or pass the result directly into a tween; GSAP will call it per target with index, element, and array.\n\n**Config (all optional):**\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `base` | Number | Starting value. Default `0`. |\n| `amount` | Number | Total to distribute across all targets (added to base). E.g. `amount: 1` with 100 targets → 0.01 between each. Use **each** instead to set a fixed step per target. |\n| `each` | Number | Amount to add between each target (added to base). E.g. `each: 1` with 4 targets → 0, 1, 2, 3. Use **amount** instead to split a total. |\n| `from` | Number \\| String \\| Array | Where distribution starts: index, or `\"start\"`, `\"center\"`, `\"edges\"`, `\"random\"`, `\"end\"`, or ratios like `[0.25, 0.75]`. Default `0`. |\n| `grid` | String \\| Array | Use grid position instead of flat index: `[rows, columns]` (e.g. `[5, 10]`) or `\"auto\"` to detect. Omit for flat array. |\n| `axis` | String | For grid: limit to one axis (`\"x\"` or `\"y\"`). |\n| `ease` | Ease | Distribute values along an ease curve (e.g. `\"power1.inOut\"`). Default `\"none\"`. |\n\n**In a tween:** pass the result of `distribute(config)` as the property value; GSAP calls the function for each target with `(index, target, targets)`.\n\n```javascript\n\u002F\u002F Scale: middle elements 0.5, outer edges 3 (amount 2.5 distributed from center)\ngsap.to(\".class\", {\n  scale: gsap.utils.distribute({\n    base: 0.5,\n    amount: 2.5,\n    from: \"center\"\n  })\n});\n```\n\n**Manual use:** call the returned function with `(index, target, targets)` to get the value for that index.\n\n```javascript\nconst distributor = gsap.utils.distribute({\n  base: 50,\n  amount: 100,\n  from: \"center\",\n  ease: \"power1.inOut\"\n});\nconst targets = gsap.utils.toArray(\".box\");\nconst valueForIndex2 = distributor(2, targets[2], targets);\n```\n\nSee [distribute()](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FGSAP\u002FUtilityMethods\u002Fdistribute\u002F) for more.\n\n## Units and Parsing\n\n### getUnit(value)\n\nReturns the unit string of a value (e.g. `\"px\"`, `\"%\"`, `\"deg\"`). Use when normalizing or converting values.\n\n```javascript\ngsap.utils.getUnit(\"100px\");   \u002F\u002F \"px\"\ngsap.utils.getUnit(\"50%\");     \u002F\u002F \"%\"\ngsap.utils.getUnit(42);        \u002F\u002F \"\" (unitless)\n```\n\n### unitize(value, unit)\n\nAppends a unit to a number, or returns the value as-is if it already has a unit. Use when building CSS values or tween end values.\n\n```javascript\ngsap.utils.unitize(100, \"px\");  \u002F\u002F \"100px\"\ngsap.utils.unitize(\"2rem\", \"px\"); \u002F\u002F \"2rem\" (unchanged)\n```\n\n### splitColor(color, returnHSL?)\n\nConverts a color string into an array: **[red, green, blue]** (0–255), or **[red, green, blue, alpha]** (4 elements for RGBA when alpha is present or required). Pass **true** as the second argument (**returnHSL**) to get **[hue, saturation, lightness]** or **[hue, saturation, lightness, alpha]** (HSL\u002FHSLA) instead. Works with `\"rgb()\"`, `\"rgba()\"`, `\"hsl()\"`, `\"hsla()\"`, hex, and named colors (e.g. `\"red\"`). Use when animating color components or building gradients. See [splitColor()](https:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FGSAP\u002FUtilityMethods\u002FsplitColor\u002F).\n\n```javascript\ngsap.utils.splitColor(\"red\");                    \u002F\u002F [255, 0, 0]\ngsap.utils.splitColor(\"#6fb936\");                \u002F\u002F [111, 185, 54]\ngsap.utils.splitColor(\"rgba(204, 153, 51, 0.5)\"); \u002F\u002F [204, 153, 51, 0.5] (4 elements)\ngsap.utils.splitColor(\"#6fb936\", true);          \u002F\u002F [94, 55, 47] (HSL: hue, saturation, lightness)\n```\n\n## Arrays and Collections\n\n### selector(scope)\n\nReturns a scoped selector function that finds elements only within the given element (or ref). Use in components so selectors like `\".box\"` match only descendants of that component, not the whole document. Accepts a DOM element or a ref (e.g. React ref; handles `.current`).\n\n```javascript\nconst q = gsap.utils.selector(containerRef);\nq(\".box\");        \u002F\u002F array of .box elements inside container\ngsap.to(q(\".circle\"), { x: 100 });\n```\n\n### toArray(value, scope?)\n\nConverts a value to an array: selector string (scoped to element), NodeList, HTMLCollection, single element, or array. Use when passing mixed inputs to GSAP (e.g. targets) and a true array is needed.\n\n```javascript\ngsap.utils.toArray(\".item\");           \u002F\u002F array of elements\ngsap.utils.toArray(\".item\", container); \u002F\u002F scoped to container\ngsap.utils.toArray(nodeList);          \u002F\u002F [ ... ] from NodeList\n```\n\n### pipe(...functions)\n\nComposes functions: **pipe(f1, f2, f3)(value)** returns f3(f2(f1(value))). Use when applying a chain of transforms (e.g. normalize → mapRange → snap) in a tween or callback.\n\n```javascript\nconst fn = gsap.utils.pipe(\n  (v) => gsap.utils.normalize(0, 100, v),\n  (v) => gsap.utils.snap(0.1, v)\n);\nfn(50); \u002F\u002F normalized then snapped\n```\n\n### wrap(min, max, value?)\n\nWraps a value into the range min–max (inclusive min, exclusive max). Use for infinite scroll or cyclic values. Omit **value** to get a function: `wrap(min, max)(value)`.\n\n```javascript\ngsap.utils.wrap(0, 360, 370);  \u002F\u002F 10\ngsap.utils.wrap(0, 360, -10);   \u002F\u002F 350\n\nlet wrapFn = gsap.utils.wrap(0, 360);\nwrapFn(370); \u002F\u002F 10\n```\n\n### wrapYoyo(min, max, value?)\n\nWraps value in range with a yoyo (bounces at ends). Use for back-and-forth within a range. Omit **value** to get a function: `wrapYoyo(min, max)(value)`.\n\n```javascript\ngsap.utils.wrapYoyo(0, 100, 150); \u002F\u002F 50 (bounces back)\n\nlet wrapY = gsap.utils.wrapYoyo(0, 100);\nwrapY(150); \u002F\u002F 50\n```\n\n## Best practices\n\n- ✅ Omit the value argument to get a reusable function when the same range\u002Fconfig is used many times (e.g. scroll handler, tween callback): `let mapFn = gsap.utils.mapRange(0, 1, 0, 360); mapFn(progress)`.\n- ✅ Use **snap** for grid-aligned or step-based values; use **toArray** when GSAP or your code needs a real array from a selector or NodeList.\n- ✅ Use **gsap.utils.selector(scope)** in components so selectors are scoped to a container or ref.\n\n## Do Not\n\n- ❌ Assume **mapRange** \u002F **normalize** handle units; they work on numbers. Use **getUnit** \u002F **unitize** when units matter.\n- ❌ Override or rely on undocumented behavior; stick to the documented API.\n\n### Learn More\n\nhttps:\u002F\u002Fgsap.com\u002Fdocs\u002Fv3\u002FHelperFunctions\n",{"name":5,"license":14,"description":7},"main","cec47adba879a68fd98365087687192fadc06985",".claude\u002Fskills\u002Fgsap-utils","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo","https:\u002F\u002Fgrumbledemo.vercel.app","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte","2026-07-16 01:34:44",{"source":8,"skillName":5,"commands":31,"prompt":41,"zipAvailable":42},[32,35,38],{"tool":33,"command":34},"npx","npx skills add stickmanbenn-byte\u002Fgrumbledemo --skill gsap-utils",{"tool":36,"command":37},"bunx","bunx skills add stickmanbenn-byte\u002Fgrumbledemo --skill gsap-utils",{"tool":39,"command":40},"pnpm","pnpm dlx skills add stickmanbenn-byte\u002Fgrumbledemo --skill gsap-utils","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgsap-utils\u002FSKILL.md",true,[],[45,54,62,70,78,86],{"skillNo":46,"name":47,"nameZh":6,"description":48,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":49,"licenseFileUrl":50,"sourceHtmlUrl":51,"categories":52,"keywords":53,"publishedAt":18},"01M794KAVYWS3651AFHW3JB0QZ","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fwebapp-testing\u002FSKILL.md",[],[],{"skillNo":55,"name":56,"nameZh":6,"description":57,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":49,"licenseFileUrl":58,"sourceHtmlUrl":59,"categories":60,"keywords":61,"publishedAt":18},"01M794K1G1A74PS5ZJVJ0YTKPV","web-artifacts-builder","Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn\u002Fui). Use for complex artifacts requiring state management, routing, or shadcn\u002Fui components - not for simple single-file HTML\u002FJSX artifacts.","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fweb-artifacts-builder\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fweb-artifacts-builder\u002FSKILL.md",[],[],{"skillNo":63,"name":64,"nameZh":6,"description":65,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":66,"sourceHtmlUrl":67,"categories":68,"keywords":69,"publishedAt":18},"01M794JNN8T6APX8TRV15NJ15Y","ui-styling","Create beautiful, accessible user interfaces with shadcn\u002Fui components (built on Radix UI + Tailwind), Tailwind CSS utility-first styling, and canvas-based visual designs. Use when building user interfaces, implementing design systems, creating responsive layouts, adding accessible components (dialogs, dropdowns, forms, tables), customizing themes and colors, implementing dark mode, generating visual designs and posters, or establishing consistent styling patterns across applications.","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fui-styling\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fui-styling\u002FSKILL.md",[],[],{"skillNo":71,"name":72,"nameZh":6,"description":73,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":49,"licenseFileUrl":74,"sourceHtmlUrl":75,"categories":76,"keywords":77,"publishedAt":18},"01M794JCJ420QYKBK6XE7C407W","theme-factory","Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors\u002Ffonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Ftheme-factory\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Ftheme-factory\u002FSKILL.md",[],[],{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":49,"licenseFileUrl":82,"sourceHtmlUrl":83,"categories":84,"keywords":85,"publishedAt":18},"01M794HPBVYNQWENKBP01KZCJF","slack-gif-creator","Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like \"make me a GIF of X doing Y for Slack.\"","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fslack-gif-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fslack-gif-creator\u002FSKILL.md",[],[],{"skillNo":87,"name":88,"nameZh":6,"description":89,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":49,"licenseFileUrl":90,"sourceHtmlUrl":91,"categories":92,"keywords":93,"publishedAt":18},"01M794HGSWNDPRV59SZ2SM9YZW","skill-creator","Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fskill-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fstickmanbenn-byte\u002Fgrumbledemo\u002Fblob\u002Fmain\u002Fagent\u002Fskills\u002Fskill-creator\u002FSKILL.md",[],[]]