The Bug:
Closed captions continue to fade when their sound plays again from farther away, causing visual delays in the overlay.
Steps to Reproduce:
Create a single player world, ensure that you have subtitles enabled, and stand still.
Play the "minecraft:block.stone.place" sound nearby, then farther away, by using the commands provided below. Enter them one after the other, within three seconds. If more than three seconds pass, the first sound is forgotten and nothing unusual happens.
/playsound minecraft:block.stone.place master @s ~2 ~ ~ 1 1 /playsound minecraft:block.stone.place master @s ~12 ~ ~ 1 1Watch your subtitles overlay closely as you enter the second command, and keep watching for a second or two afterwards.
Observed Behavior:
The "Block placed" subtitle does not react to the second sound, and instead keeps fading before jumping brighter about a second later with nothing playing.
Expected Behavior:
The "Block placed" subtitle would return to full brightness the instant the second sound plays.
Code Analysis:
The following is based on a decompiled version of Minecraft 26.3 Snapshot 4 using Mojang mappings.
A subtitle keeps a list of every place its sound has played and when it played there. When the text is drawn, getClosest picks the entry nearest the player. That one entry decides two things, the arrow showing which way to look, and how faded the text is.
net.minecraft.client.gui.components.SubtitleOverlay.java
SubtitleOverlay.SoundPlayedAt closestRecentLocation = subtitle.getClosest(position);
if (closestRecentLocation != null) {
...
int brightness = Mth.floor(
Mth.clampedLerp((float)(Util.getMillis() - closestRecentLocation.time) / (float)(3000.0 * displayTimeMultiplier), 255.0F, 75.0F)
);
...
public SubtitleOverlay.@Nullable SoundPlayedAt getClosest(final Vec3 position) {
if (this.playedAt.isEmpty()) {
return null;
} else {
return this.playedAt.size() == 1
? this.playedAt.getFirst()
: this.playedAt.stream().min(Comparator.comparingDouble(soundPlayedAt -> soundPlayedAt.location().distanceTo(position))).orElse(null);
}
}If we look at the above class, we can see that getClosest sorts the entries by distance and never looks at when the sound played. The fade is then read off time, the moment the sound played there, which is a question about how long ago, not how far away. So a nearer, older entry keeps holding the fade. The text only brightens once that older entry expires.
Fixed Code:
net.minecraft.client.gui.components.SubtitleOverlay.java
SubtitleOverlay.SoundPlayedAt closestRecentLocation = subtitle.getClosest(position);
if (closestRecentLocation != null) {
SubtitleOverlay.SoundPlayedAt mostRecentAudible = subtitle.getMostRecentAudible(position);
...
int brightness = Mth.floor(
Mth.clampedLerp((float)(Util.getMillis() - mostRecentAudible.time) / (float)(3000.0 * displayTimeMultiplier), 255.0F, 75.0F)
);
...
public SubtitleOverlay.@Nullable SoundPlayedAt getMostRecentAudible(final Vec3 position) {
return this.playedAt
.stream()
.filter(soundPlayedAt -> position.closerThan(soundPlayedAt.location(), this.range))
.max(Comparator.comparingLong(SubtitleOverlay.SoundPlayedAt::time))
.orElseGet(() -> this.getClosest(position));
}The above adds a second lookup next to getClosest. It picks the most recent entry close enough to hear, falling back to the nearest one if none is, and the fade is read off that one instead. The arrow still uses the nearest entry, since that is the one being heard.
Simply taking the newest entry wherever it happened would have been shorter. It was not used because the game reports sounds the player cannot actually hear. A distant one would keep resetting the text to full brightness, telling players who rely on subtitles about a sound they cannot hear. The closerThan check keeps only the entries within the sound's own range, the distance it carries.
This change was applied to a modified client and tested against the steps above, and the subtitle now returns to full brightness the instant the second sound plays.
I am able to reproduce the reported behavior. It was introduced in 24w19a, presumably alongside the fix of MC-267981. This issue is not limited to Multiplayer, but can also be reproduced in Singleplayer.