Kabir's solution is correct. My image URL was
/images/ads/homepage/small-banners01.png,
and this was tripping up AdBlock. This wasn't a cross-domain issue for me, and it failed on both localhost and on the web.
I was using Chrome's network tab to debug and finding very confusing results for these specific images that failed to load. The first request would return no response (Status "(pending)"). Later down the line, there was a second request that listed the original URL and then "Redirect" as the Initiator. The redirect request headers were all for this identical short line of base64-encoded data, and each returned no response, although the status was "Successful":
GET data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg== HTTP/1.1
Later I noticed that these inline styles were added to all the image elements:
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
Finally, I did not receive any "failed to load resource" messages in the console, but rather this:
Port error: Could not establish connection. Receiving end does not exist.
If any of these things is happening to you, it probably has something to do with AdBlock. Turn it off and/or rename your image files.
Also, because of the inline CSS created by AdBlock, the layout of my promotions slider was being thrown off. While I was able to fix the layout issues with CSS before finding Kabir's solution, the CSS was somewhat unnecessary and affected the flexibility of the slider to handle images of multiple sizes.
I guess the lesson is: Be careful what you name your images. These images weren't malicious or annoying as much as they were alerting visitors to current promotions and specials in an unobtrusive way.