Last updated Jun 08, 2026 at 08:46pm
This started because we were building the online cursor editor and ran into something that made no sense. Some animated cursor exports would load fine in Windows. Others looked fine in previews, looked fine in the file structure, and then Windows just refused to load them.
Just Found Out Windows Supports Multi-Size Animated Cursors
TLDR
Windows really can load animated .ani files that contain multiple sizes per frame, kind of like multi-size .cur files.
The problem is that Windows does not handle those files consistently. In our testing, some valid multi-size animated cursors loaded and some did not. The pass/fail line depended on a mix of:
- which sizes were in the file
- how many size pages were in each frame
- how dense the image data was
- how many frames the animation had
- whether the pages were stored as DIB or PNG
So this is not some clean rule like "Windows only supports 5 sizes." It is messier than that. However, out of this research and experiment we were able to standardize our site and come out with a new rule for delivering animated cursors.
All future animated cursors will come in sizes like below if max size is 256x256:
- Default:
32,48,64 - Large:
72,96,128 - Extra Large:
256
ELI5
Think of an animated cursor like a flipbook.
Each frame can hold one image, or it can hold a few versions of the same image at different sizes, like 32x32, 48x48, and 64x64.
In theory, Windows should pick the best one for the user's DPI and show a sharp cursor.
And sometimes it actually does.
But once you start mixing certain sizes together, Windows can just decide it hates your file and refuse to load it. No useful error. No explanation. It just fails.
Why We Even Cared And How We Fell Into This Hole
We are building a browser-based cursor editor for Cursors-4U.com that exports both .cur and .ani.
For static .cur files, multi-size export was easy. Windows handles it fine. You get better DPI scaling. No drama.
The problem started during animated export.
We would make an animated cursor in the editor, export it, and it would look fine in our own previews. The file would look structurally valid. Sometimes it would even look normal when inspected outside of Windows' actual cursor loader path. But then Windows itself would refuse to use it. Another cursor exported almost the same way would load fine.
That is what kicked this whole thing off.
At first it looked random. Then it started looking like a pattern. Then it turned into a full rabbit hole because from the site's point of view, we cannot ship an export button that works for one animated cursor and silently breaks for the next one.
We also cared for a practical reason: standardization.
If multi-size animated .ani worked reliably, we would not need to generate a stupid number of separate animated files just to cover common DPI ranges. That matters because it affects:
- storage
- processing
- bandwidth
- ZIP size
- how clean the whole export system is
The editor is still a work in progress. If the current schedule holds, the target is May 2026.
Before The Testing: DPI Scaling vs Accessibility Pointer Size Primer
One thing that is easy to mix up here is that Windows has two different size systems that can both make the cursor appear larger.
The first is normal display / DPI scaling.
That is the regular Windows scaling setting for the whole display, like 100%, 125%, 150%, 200%, and so on. Microsoft also allows custom scaling between 100% and 500%. So if you start from a 32x32 cursor, the DPI-scaled displayed size can plausibly range from about 32x32 up to about 160x160.
The second is the Accessibility pointer size slider.
That is a separate setting from display scale. In the sizing model we use for the editor, the 1..15 slider range maps like this:
1 = 322 = 483 = 644 = 805 = 966 = 1127 = 1288 = 1449 = 16010 = 17611 = 19212 = 20813 = 22414 = 24015 = 256
The important part is that these are not the same thing.
Windows has a DPI scaling path, and it also has a separate accessibility size path.
From direct observation, the accessibility pointer-size behavior appears to add some extra size on top of DPI scaling, but it does not appear to multiply the DPI-scaled result. Our site has a max size of 256x256, but windows can go higher than this when DPI and Accessibility and mixed together.
So the safe takeaway is:
- DPI scaling can go much higher than people usually think
- the accessibility slider can also push cursor size much higher than the default range
- that is exactly why larger cursor resources matter, even before you get into the weird ANI loader behavior
Quick History
There are really two different questions here:
- When did Windows support
.aniat all? - When did multi-size animated cursors start making sense as a DPI feature?
For basic .ani support, Microsoft's LoadCursorFromFile docs list Windows 2000 Professional as the minimum supported client. Microsoft's 2007 animated-cursor security advisory and MS07-017 also hit Windows 2000, XP, Server 2003, and Vista, so animated cursor loading definitely existed well before Windows 7.
For multiple images inside cursor resources, Microsoft's CURSOR resource docs say cursor resources can contain more than one image. LookupIconIdFromDirectoryEx also documents the best-fit image selection logic.
For multi-resolution animated cursors as a practical DPI feature, the best evidence points to the Windows 7 era. RealWorld's Multi-resolution Windows 7 cursors page and its cursor editor page both point that way. That is not an official Microsoft "introduced in Windows 7" statement, so I am not pretending it is. But it lines up with what we found.
Just Found Out It's Buggy as Hell
Why We Had To Start Testing So Hard
Once we realized the editor could export two animated cursors that looked basically fine to us, but only one of them would actually load in Windows, we had to stop guessing.
Preview images were not enough.
"The file parses" was not enough.
"It opened in a tool" was not enough.
The only thing that mattered was whether Windows itself accepted the file.
That is why the testing got more serious.
How We Tested It
We built a Python harness that generated .ani files while changing:
- number of size pages per frame
- exact dimensions
- DIB vs PNG encoding
- frame count
- sparse vs dense payloads
- real cursor art vs synthetic images
We tested files in a few ways:
- by browsing to them in Mouse Properties > Pointers
- by calling the real Windows
LoadCursorFromFileWAPI - by following up with
LoadImageandSetThreadCursorCreationScalingto make sure this was not just a DPI-picking problem
We also checked the failing files structurally:
- RIFF layout
anihfields- offsets
- hotspot data
- image sizes
- padding and alignment
The direct loader tests were done on:
- Windows 11
23H2 - WSL2 Ubuntu for generation and analysis
- Python for structural checks
- PowerShell to hit the real Windows loader
The First Real Boundary We Hit
One of the first useful tests was not some giant fuzzing matrix. It was five real sample exports from the same cursor series, all using the same animation and artwork, but with more size pages added each time.
| Sample | Sizes Per Frame | Result |
|---|---|---|
| 1 | 256 | Loads |
| 2 | 32,48,256 | Loads |
| 3 | 32,48,64,256 | Loads |
| 4 | 32,48,64,72,256 | Loads |
| 5 | 32,48,64,72,96,256 | Fails |
Then we made a copy of the failing file and removed only 256.
That new file loaded.
So right away, it was obvious this was not just "Windows hates 96" or "Windows hates 256." It was the combination.
The Patterns That Actually Mattered
These were the big ones:
- single-size
.aniworked reliably - static multi-size
.curworked reliably - some multi-size animated
.anifiles loaded - other multi-size animated
.anifiles with valid structure failed silently
And the silent part is what made it so annoying. Windows did not tell us what was wrong. In the direct API tests it usually just returned a null handle.
Some of the strongest patterns:
- DIB-heavy animated multi-size files failed a lot more often than PNG-only ones
- dense PNG-only
6-page sets could still fail - the same exact multi-size frame payload could load as a static
.curand fail as a1-frame.ani - changing requested size with
LoadImagedid not save failing files - changing thread cursor scaling with
SetThreadCursorCreationScalingdid not save failing files - high-only size groups above
128were especially sketchy
One of the strongest repros was this:
- take frame
0from a failing ANI - save that exact frame as a standalone multi-size
.cur - Windows loads the
.cur - wrap that same payload back into a
1-frame.ani - Windows rejects the
.ani
That is a pretty strong sign that this is not just "oops, bad file."
The Most Useful Loader Results
This is the smaller set of results that really shaped the final decision.
| Test | Setup | Result |
|---|---|---|
| Sparse PNG stress file | 32..256, 225 pages per frame | Loads |
| Fixed size set | 32,48,64,72,96,256, 14 frames, all-PNG sparse | Loads |
| Fixed size set | 32,48,64,72,96,256, 14 frames, all-PNG dense | Fails |
| Fixed size set | 32,48,64,72,96,256, 14 frames, mixed DIB + PNG | Fails |
| Fixed size set | 32,48,64,72,96,256, 14 frames, all-DIB | Fails |
| Dense PNG boundary | 32,48,64,72,max, 2 frames, max=96..256 | All loaded |
| Dense PNG boundary | 32,48,64,72,96,max, 2 frames, max=128..256 | All failed |
That is where it stopped looking like a simple size-count problem and started looking like some ugly combination of size mix, payload density, and loader path.
What the Internet Was Good For
The docs were useful for confirming the basics, not for explaining the bug.
What the docs do support:
LoadCursorFromFilesupports.curand.aniCURSOR resourcesupports more than one imageLookupIconIdFromDirectoryExdocuments best-fit image selection
The community stuff was mixed.
Older RealWorld forum posts blamed some ANI failures on 128x128, but our testing showed that was not the whole story.
The most useful outside clue was this RealWorld thread about hybrid BMP/DIB plus PNG cursor resources, where the author says Windows 10/11 gets weird when only the larger pages are PNG-compressed. That matched part of what we saw, especially with mixed DIB+PNG files.
Microsoft's MS07-017 was also a useful reminder that ANI handling has had hardened validation logic for a long time. It does not explain our exact issue, but it makes "there is some weird internal validator here" feel very believable.
What we did not find:
- no official Microsoft page documenting an ANI-specific multi-size limit
- no community post that cleanly described the exact failure pattern we reproduced
What We Think the Problem Is
At this point, the best explanation is not "the format says no."
The better explanation is:
- Windows has an ANI-specific loader or validation quirk
- it probably has more than one trigger
- the triggers seem to involve some mix of:
- size combination
- page count per frame
- frame count
- payload density
- DIB vs PNG code paths
Why we think that:
- the failing files were structurally valid
LoadCursorFromFileWandLoadImageagreed on the same failures- explicit requested sizes did not help
- thread cursor scaling changes did not help
- the same exact frame payload could load as
.curand fail as.ani
So the honest answer is:
This is probably a Windows bug, or at least some undocumented loader validation behavior deep enough that black-box testing can only narrow it down, not fully explain it.
If You Actually Know the Answer
If you know the exact internal reason this happens, I would love to know it.
The useful kinds of expertise here would be:
- Windows cursor internals
user32.dllcursor loading- ANI parsing or validation internals
- cursor theme authoring on modern Windows
- reverse engineering the animated cursor loader path
Just Found Out How To Standardize This On Our Site
The Size Presets That Actually Made Sense
At some point the question stopped being:
"Why is Windows like this?"
And became:
"What can we actually ship that still saves resources and does not randomly break?"
The most defensible grouped presets from the testing were:
| Role | Sizes | Result | Why We Kept It |
|---|---|---|---|
| Default | 32,48,64 | Loads | Clean low-range preset that kept working |
| Large | 72,96,128 | Loads | Good middle-range preset that also kept working |
| Extra Large | 256 | Loads | Simpler and safer than grouped high-range presets |
We also had follow-up tests where 128,256 loaded, but once we kept pushing deeper into grouped high-range presets above 128, things got shaky again fast. That is why we did not build the final standard around "lots of big grouped sizes." It was not stable enough.
The Full Combo List From Our Notes
I went back through the saved research notes and the later follow-up test thread and pulled the combinations into one place.
This is not every possible size set in the world. It is the full set of combinations we explicitly wrote down while doing this research. Unless a row says otherwise, these were direct Windows loader checks on the same Windows 11 23H2 machine.
Real Export Series
These were five real animated exports from the same cursor series. Same artwork. Same animation. More sizes added each time.
| Sizes | Result | Notes |
|---|---|---|
256 | Loads | Real sample export |
32,48,256 | Loads | Real sample export |
32,48,64,256 | Loads | Real sample export |
32,48,64,72,256 | Loads | Real sample export |
32,48,64,72,96,256 | Fails | Real sample export |
32,48,64,72,96 | Loads | Failing sample with only 256 removed |
DIB And Mixed Boundary Combos
These were the early combinations that made it obvious this was not just "Windows hates 128" or "Windows hates 256."
| Sizes | Encoding / Setup | Result |
|---|---|---|
32-43 | consecutive DIB | Loads |
32-44 | consecutive DIB | Fails |
42-49 | consecutive DIB | Loads |
42-50 | consecutive DIB | Fails |
52-57 | consecutive DIB | Loads |
62-65 | consecutive DIB | Loads |
62-66 | consecutive DIB | Fails |
72-74 | consecutive DIB | Loads |
82-84 | consecutive DIB | Loads |
232 | consecutive DIB | Loads |
232-233 | consecutive DIB | Fails |
32,48,64,72,96 | spaced DIB | Loads |
32,48,64,72,128 | spaced DIB | Loads |
32,48,64,96,128 | spaced DIB | Fails |
32,48,64,72,96,128 | spaced DIB | Fails |
256 | PNG-only page | Loads |
32,48,256 | mixed DIB + PNG | Loads |
32,48,64,256 | mixed DIB + PNG | Loads |
32,48,64,72,256 | mixed DIB + PNG | Loads |
32,48,64,72,96,256 | mixed DIB + PNG | Fails |
32,48,64,72,256 | dense PNG-only, 2 frames | Loads |
32,48,64,72,96,256 | dense PNG-only, 2 frames | Fails |
Dense PNG Sweep Results
These were useful because they showed that "PNG-only" still was not enough by itself.
All of these loaded
32,48,64,72,9632,48,64,72,12832,48,64,72,14432,48,64,72,16032,48,64,72,19232,48,64,72,22432,48,64,72,256
All of these failed
32,48,64,72,96,12832,48,64,72,96,14432,48,64,72,96,16032,48,64,72,96,19232,48,64,72,96,22432,48,64,72,96,256
We also had the 14-frame version of this same pattern:
32,48,64,72,96loaded32,48,64,72,96,128failed
Sparse PNG Stress Files
These loaded:
32..144with113pages per frame,2frames, one visible pixel per page32..248with217pages per frame32..256with225pages per frame
So page count alone was clearly not the whole story.
Grouped Preset Follow-Ups
This is where the "just use 3 sizes" theory broke down. Some 3-size groups worked. Some absolutely did not.
| Sizes | Result |
|---|---|
32,48,64 | Loads |
72,96,128 | Loads |
128,256 | Loads |
96,128,256 | Fails |
128,144,256 | Fails |
128,160,256 | Fails |
128,176,256 | Fails |
128,192,256 | Fails |
128,224,256 | Fails |
72,96,112,128 | Fails |
72,96,128,256 | Fails |
96,128,192,256 | Fails |
128,160,192,256 | Fails |
So no, "3 sizes always works" was not true. Exact size choice still mattered.
Low-Range PNG-Only Follow-Ups
After all the weird mid-range and high-range failures, we went back and tested the low end more aggressively.
First, we ran a full PNG-only matrix on a 4px grid from 32 to 64:
32,36,40,44,48,52,56,60,64- every
3-size combination - every
4-size combination - every
5-size combination - every
6-size combination - dense synthetic payload
- tested at both
2frames and14frames
That was 420 ANI files per frame-count pass, 840 real Windows loader checks total.
Result:
| Size Count | 2 Frames | 14 Frames |
|---|---|---|
3 | 84/84 passed | 84/84 passed |
4 | 126/126 passed | 126/126 passed |
5 | 126/126 passed | 126/126 passed |
6 | 84/84 passed | 84/84 passed |
So low-range PNG-only combinations were much more stable than the mid/high-range combinations.
Then we pushed a different low-range shape: a single ANI containing every consecutive size from 32 upward.
For that consecutive PNG-only ladder, the break point was:
32..46(15entries) loaded32..47(16entries) failed
And that was true at both 2 frames and 14 frames.
Here is the full cutoff ladder from that test:
| Consecutive Sizes | Entry Count | 2 Frames | 14 Frames |
|---|---|---|---|
32..38 | 7 | Loads | Loads |
32..39 | 8 | Loads | Loads |
32..40 | 9 | Loads | Loads |
32..41 | 10 | Loads | Loads |
32..42 | 11 | Loads | Loads |
32..43 | 12 | Loads | Loads |
32..44 | 13 | Loads | Loads |
32..45 | 14 | Loads | Loads |
32..46 | 15 | Loads | Loads |
32..47 | 16 | Fails | Fails |
32..48 | 17 | Fails | Fails |
32..49 | 18 | Fails | Fails |
32..50 | 19 | Fails | Fails |
32..51 | 20 | Fails | Fails |
32..52 | 21 | Fails | Fails |
32..53 | 22 | Fails | Fails |
32..54 | 23 | Fails | Fails |
32..55 | 24 | Fails | Fails |
32..56 | 25 | Fails | Fails |
32..57 | 26 | Fails | Fails |
32..58 | 27 | Fails | Fails |
32..59 | 28 | Fails | Fails |
32..60 | 29 | Fails | Fails |
32..61 | 30 | Fails | Fails |
32..62 | 31 | Fails | Fails |
32..63 | 32 | Fails | Fails |
32..64 | 33 | Fails | Fails |
So the low end turned out to have a much wider safe zone than we expected, but not an unlimited one.
High-Only Combos Above 128
We also ran a focused PNG-only scan using only sizes above 128. Every one of these failed at both 2 frames and 14 frames.
2-size high-only combos that failed
144,160144,176144,192144,224144,256160,176160,192160,224160,256176,192176,224176,256192,224192,256224,256
3-size high-only combos that failed
144,160,176144,160,192144,160,224144,160,256144,176,192144,176,224144,176,256144,192,224144,192,256144,224,256160,176,192160,176,224160,176,256160,192,224160,192,256160,224,256176,192,224176,192,256176,224,256192,224,256
Smaller Follow-Up Combos From The Same Research Thread
These were later spot checks on the same Windows 11 box. I am listing them separately because they were not part of the earlier saved harness tables, but they are still part of the research trail from this same project.
Reported as loaded in follow-up checks
32,48,7232,48,9632,64,7232,64,9632,72,9632,48,64,7232,48,64,9632,48,72,9632,64,72,9632,48,12832,64,12832,64,96,12832,48,25632,64,25632,96,25632,48,64,25632,48,96,25632,64,96,25632,48,80,11232,56,80,10472,96,11280,96,128
Those later spot checks are part of why the site ended up standardizing on a short list of exact presets instead of letting people build whatever multi-size animated combination they want.
What We Are Doing Now
We started this whole thing hoping for one nice multi-size animated export that could cover everything from 32 to 256 in a single ANI.
We did not get that.
What we got instead was a more realistic rule:
- keep animated multi-size ANI PNG-only
- stop allowing random freeform animated size combinations
- standardize around three animated size tiers
- use standalone
256as the extra-large fallback instead of trying to get fancy in the high range
Those three tiers are:
- Default:
32,48,64 - Large:
72,96,128 - Extra Large:
256
That is also what the ingester/export rules reflect right now.
That still gives Windows something large to downscale from when it needs to, which is a safer bet than asking the ANI loader to accept a bunch of shaky grouped high-range presets.
So yeah, the final lesson is kind of annoying:
Windows does support multi-size animated cursors.
It just does not support them cleanly enough to trust a freeform exporter that our editor wanted. BTW, who's using cursor sizes 128x128 or higher? Leave a comment if you are.
Sources
LoadCursorFromFileLoadImageSetThreadCursorCreationScaling- Microsoft Support: Make Windows easier to see
CURSOR resourceCURSORDIRLookupIconIdFromDirectoryEx- Microsoft Security Bulletin MS07-017
- RealWorld: Multi-resolution Windows 7 cursors
- RealWorld: Cursor editor / Windows 7 multi-resolution animated cursors
- RealWorld forum: hybrid compression issue
Comments