A downloadable asset pack for Windows, macOS, and Linux

Download NowName your own price

This project is made to demonstrate an effect I'm calling, Auto Highlight.  Its primary purpose is to allow you to subtly change sprites based on who is speaking in the dialogue, drawing attention to the speaker's sprite. All handled automatically, saving you from having to write ATL on every line of the script. The default implementation of this I've provided makes use of a small zoom and darkening of the sprite. But can easily be expanded to make use of other ATL properties based on the desired effect. 

The main file of interest is the 00auto-highlight.rpy. It provides several comments to explain how it works and what you'll need to do to get it running. Be sure to read the setup comments near the top to know what you have to do to implement it.

Originally this was a minor effect that I created for Where the Demons Lurk. But it ended up being useful for both Beyond the Harbor: r and Burrows. As a result, I began to consider that it might be useful for other projects as well. So, I decided to release it so everyone has access to it.

Default implementation makes use of the matrixcolor ATL property, which is only available on Renpy 7.4 and above. But can be modified to work with earlier versions if you desire. 

The code for this project is provided under the MIT license. 


Luke and Kota sprites used with permission from https://minoh.itch.io/minotaur-hotel-sfw I do not claim ownership of the sprites. The sprites belong to their respective owners.

Demo script written by members of FVN in a voice call: https://discord.gg/GFjSPkh - FVN is an 18+ discord server. Do not join if you are a minor.

Beyond the Harbor: r Example from: https://harmoyena.itch.io/beyond-the-harbor - BtH:r is an 18+ VN with strong themes that are inappropriate for children

Burrows Example is from: https://itch.io/search?q=Burrows - Burrows is an 18+ VN with strong themes that are inappropriate for children

Rated 4.9 out of 5 stars
(39 total ratings)
GenreVisual Novel
Made withRen'Py
TagsAsset Pack, Ren'Py, sourcecode
Code licenseMIT License
Average sessionA few seconds
InputsKeyboard, Mouse


Download NowName your own price

Click download now to get access to the following files:

00auto-highlight.rpy 12 kB
AutoHighlight-1.0-pc.zip 50 MB
AutoHighlight-1.0-mac.zip 15 MB


Log in with itch.io to leave a comment.

Hi Wattson, thanks for the content!

I have a question with making a callback to a character when the player has input their own name. I've been trying a few things but nothing is working. Any ideas what I need to put instead of the %(P)s  ? Thanks anyone in advance.

This is my code:  

(WORKING FINE) define t = Character('Topaz', image='i_topaz.png', color="#d2be0a", callback=name_callback, cb_name='t') image i_topaz =At('i_topaz.png', sprite_highlight('t'))

define P = Character('%(P)s', image='i_mc1l.png', color="#e712b2", callback=name_callback, cb_name='%(P)s') image i_mc1l =At('i_mc1l.png', sprite_highlight('%(P)s'))

This is super helpful and works great.
I have a question though, is it possible to manually highlight a character?
Like, if a character is not speaking, but the narrator is describing their action, such as "Then CHARACTER laughed out loud." I want CHARACTER to be highlighted.

I was dumb, the answer was in the code's comments. If anyone else wants to know how to do this, it just takes 2 lines.
Use this line so that no one will be highlighted during narration:
define narrator = Character(callback = name_callback, cb_name = None)
Then just define who you want highlighted like this:
"Then CHARACTER laughed out loud." (cb_name = "CHARACTER")

Thank you very much! This plugin helped me a lot!

Hello! I was wondering if there was a way to adjust a sprite's zoom levels globally, while also using this plugin. I don't mean the zoom_change, but rather a zoom for the sprite overall. For example, I have the following code:

transform small:
     zoom 0.8

And I can apply that to any of the character sprites to make them a little smaller. However, once I enable the auto-highlight effect on a character as follows, the zoom stops working and the sprite reverts to it's original size.

show h stunned:
    function SpriteFocus('haka')

If I take out the "function SpriteFocus('haka')" part, the "small" effect works again and the sprite becomes smaller. Is there any way I can have zoom effects work on sprites while the auto-highlighter is active?

Hello, great plugin, but I encountered a problem, at the beginning of each scene, when the dialogue starts, the picture kind of “jumps”, first a darkened picture is shown, then immediately a normal one, after which the dialogue runs normally, as it should. Any ideas what could be causing this and how to fix it?

Sample code below:

define g1 = Character("Первый Стражник", color="#be4c4c", image="guard1",callback=name_callback, cb_name="guard1")

image guard1 guard1a = At('guard1', sprite_highlight('guard1'))

show guard1 at left2

with Dissolve(0.2)

g1 guard1a "В райских гущах, а ты как думаешь?"

If I had to guess I would presume it's because when the scene starts, the character hasn't been speaking, so the speaking_char is probably either None or someone else. Then when the line starts, the character is then set as the speaking_char, so he lights up to normal. If you want them to start off already focused, you can manually set the speaking char by doing:

$ speaking_char = "guard1"

either before the transition or sometime during it. Which would then have the sprite start off in the talking state.

Hopefully that helps. But if it's some other issue, let me know and I'd be happy to help correct it.

Unfortunately it didn't help, the problem still occurs. I tried reinstalling the file, changing animation settings. If suddenly this helps in any way, after I click through the entire dialogue, and then go back and repeat everything again, the error does not occur.

Sorry about that. Yeah I've had that issue sometimes crop up. Usually though it's when I'm having another auto talking solution going on as well. If I ever find a solution to that though I'll be sure to let you know.

(1 edit)

Hey there, I've been trying to get this working for about two hours now and I even followed the zeil learnings video, is there something I might be missing that's super simple and I'm just being dumb?

I'd need to know more about your project to know what might be the issue.

I got some help with reddit, I think what I was misunderstanding was when I was to use "at sprite_highlight" and SpriteFocus

Hey there! Thanks for this tool, but I was wondering how to apply shaders to this tool. I tried applying it under line 185 as suggested in the comments, but I get a "ATLTransform object is not iterable".

if is_talking: 
    trans.matrixcolor = ...
    trans.shader = outline() # <- this is defined as a transform

Implementing the shader by hand also didn't work...

if is_talking:
    trans.matrixcolor = ...
    trans.shader = "remix.smoothstep_outline" # there is no outline

Never mind! It turns out it was an issue with the shader I was using. My bad.

Hi there! Thank you for making such a fantastic effect for visual novels. I wanted to ask about an issue I've been running into. 

Following instructions, I placed the auto-highlight file into the game, and changed the definitions to match the instructions. It works like an absolute charm on certain characters (here: mary and vein), but not on others (here: charlie and mona). 

## Define characters 
define charlie = Character("Charlie",callback=name_callback, cb_name = "charlie", image="charlie")
define mary = Character("Mary", callback=name_callback, cb_name = "mary", image="mary")
define mona = Character("Mona", callback=name_callback, cb_name = "mona", image="mona")
define vein = Character("Vein", callback=name_callback, cb_name = "vein", image="vein") ...
## Image definitions
layeredimage mona: #mona's SPRITES
    at sprite_highlight('mona') 
    group exp:         attribute base:             "images/MONA/mona.png" default         attribute shadow:             "images/MONA/monashadow.png"         ....

There are brief moments when the character's name is changed ($ mona = "???"), and this might be the problem. Even reverting the name to its original ($ mona = "Mona") still doesn't work. Are there any solutions to this issue?

(1 edit)

Got it fixed! I made a minor mistake of not capitalizing the name when converting it to a different one.

Incorrect: $ mona = "???"

Correct: $ Mona = "???"

EDIT: It broke again... a capital letter will not change the character's name now, so I would have to either decide to keep the autohighlight with no name changes or name changes with no autohighlight aughhhhh

(1 edit)

I've been trying to get this highlight to work, but whenever I call images defined by it, they don't show up. I don't know if it's because the characters are defined within a class?

(oops, not sure if images attached the first time)

Ah so you got it fixed?

Hi, thank you for this! I am using it in an upcoming project. I was wondering if you have any idea how you would be able to set this so that a button in settings can toggle it off and on?

You'd probably need to add a flag to each text tag function or class to tell it to do more default behavior. Will need to make it a persistent or preference variable as well so it is always accessible.

What a swell treat! Works like a charm, so thank you very very much for sharing it.

I was trying to run the code with this asset, and it is crushing with error "global name 'Saturation matrix' is not defined".  I'm using individual sprites, not layered one.
Can you please help me to fix this?(( 

(1 edit)

i tried changing the anim_length (to 1.0) and for some reason it only applies when the sprite is first shown, it goes back to the default (0.2) setting as i put another sprite over it.

i use individual sprites but all of them are defined.

i even set it to 0.5, wondering if it was too fast to no avail.

i use NVL, if that helps.

wondered if you could look into it.

thank you very much for this btw.


Hi. Sorry for the late reply. I tried replicating the issue on my end, but doing the same setup (or at least what I can suppose from your comment) yielded results that looked correct to me. My best guess for the issue is that something about how you defined the sprites is causing renpy to reset the anim_time value, which would cause the sprite_highlight function to snap to the highlighted value. Which maybe you're confusing for the .2 seconds given .2 is pretty fast. Though I could be wrong. This is just my best guess given the information.

I tested with NVL and that didn't seem to make much of a difference. If you'd want to show me your code so I could test it and see what the issue might be, I'd be happy to take a look when I have time. (My username on discord is wattson if you need to reach out). But yeah I'm not entirely sure what the problem might be beyond my guess. Hope this was helpful and a solution to your issue can be found!

thank you for replying!

i sent you a discord req, i'd greatly appreciate if you could take a look if you do have time for it.

i don't think it's the definition because it's the exact same for every sprite and it does work for whatever that i place on the screen first, though i'd be glad to send you the code to see for yourself.

(1 edit) (+1)

Hello! I'm Zeil from Zeil Learnings, a YouTube channel about Ren'Py. Thank you for this! It was very helpful and easy to implement thanks to your awesome documentation! If you allow me, I would like your permission to upload a modified version of this on my itch.io (not using a layered image and more comments on script.rpy). I'll link it to this page, ofc. Additionally, I'd like to feature it in one of my future videos. (maybe 2-3 months from now). You may contact me on Discord if you have any questions: _zeil 

For more context, an individual approached me seeking assistance with this. So I figured it's not easy for non-programmers to follow. Therefore, I want to make a modified version of it for people without programming experience. 



Hi. Sorry for taking a bit to get back to you. But yeah I appreciate you wanting to edit it to be easier for non-programmers to read it. I will admit I did my best when writing the original code to try and make it approachable as I could. If you have ideas on how to make it more approachable, I'd be happy to take feedback and upload a modified version on here with credit to you for the help. But if you're not making significant changes to the functionality and just adding comments, I feel like it'd be best to just have one project up. Sent a friend request on discord so we can chat more about it. Thanks for your feedback.

- Wattson

I'm sorry, I'm kinda confuse, am I only have to drag the file 00auto-highlight.rpy into my file game right?


Yes. But you'll also need to make changes to your layeredimage sprites to work with it in order for it to work. Instructions and examples on how to do it can be found in the example project.

Hi! How do I have 2 speakers at the same time? I'm only able to have 1 speaker who isn't darkened.

Would anyone know how to make this work with an im.matrix  function? I know it's a deprecated function, but some of my sprites appear darker/less saturated on Renpy than they  actually are, and this has currently been my fix for them until I can replace them. 

Can you not use matrixcolor to accomplish the same thing?

(1 edit)

I'm not sure about how to use matrixcolor on individual sprites.

Any advice on how to make this work on custom player names? It works for all of my other characters, but because I let the player choose their own name, the sprite doesn't highlight when they speak. Any thoughts?

define mc = Character("[mc_name]", callback=name_callback, cb_name="mc_light")
layeredimage mc:
    at sprite_highlight('mc_light')
    group expressions:
        attribute neutral default:
        attribute happy:
        attribute sad:
        attribute angry:
        attribute surprised:
    if blush:         "mc_blush" layeredimage mchbrunch:     at sprite_highlight('mc_light')     always:         "mc_brunch"     group expressions:         attribute neutral default:             pos(163,125)             "mc_neutral"         attribute happy:             pos(163,125)             "mc_happy"         attribute sad:             pos(163,128)             "mc_sad"         attribute angry:             pos(163,125)             "mc_angry"         attribute surprised:             pos(163,125)             "mc_surprised"     if blush2:         pos(165,115)         "mc_blush2"

I cannot, for the life of me, get this to work with layered images. It only seems to work for me if I set the function SpriteFocus('sprite_name') per sprite call/update. Here's an example of one of my images and the associated character. I have no idea why this isn't working properly. It does not highlight at all (unless I manually set the sprite focus per call, which is tedious). Thoughts?

define Richard = Character("[Richard_name]", color="#5c00ff", callback = name_callback, cb_name = "ric", image='richard')
layeredimage richard:
    at sprite_highlight('richard')
    attribute normal default:
    attribute angry:
        "Richard ANGRY"
    attribute annoyed:
        "Richard ANNOYED"
    attribute blush:
        "Richard BLUSH"
    attribute blush2:
        "Richard BLUSH 2"
    attribute crying:
        "Richard CRYING"
    attribute crying2:
        "Richard CRYING 2"
    attribute surprised:
        "Richard SURPRISED"
    attribute worried:
        "Richard WORRIED"

Figured it out! Turns out my layered images were being overridden by how I was calling the sprite later on in the script, thus the callback wasn't actually executing.

(1 edit)

How exactly did you fix the issue? I'm using layered images, too, but I'm not really sure what I'm doing wrong

Edit: Nevermind, it was working the whole time but the zoom was so low that I didn't notice the change


Highlight works fine in all places, but it doesn't work here. What could this be related to?


In what way does it not work?

The highlight on the character "Mark" does not work here, but in all other dialogues with him it works. I tried changing the names of the files, as they only have differences in names, but it didn't help.

Right now I was thinking that it doesn't work because of the previous replica of this character. In front of her I have a sprite in the middle with two characters pushing each other and Mark is talking at this time. After that, the sprite of the two characters is removed and Mark speaks again. It is at this moment that highlight does not work on the character Mark.

It is here that the backlight and its animation on the character Mark do not work:

Maybe it has something to do with the 'with' transitions?

I tried to remove the "with dissolve" transition, but the highlight animation still didn't work.

As far as I can see from the sprites, the Van character is still darkened. And the highlight animation most likely does not work just because of the previous replica of the Mark character, since I noticed that after changing sprites with pushing two characters a little further than this moment, when the Van character has a replica, the highlight stays on it for a few milliseconds and goes to the Mark character when he speaks. I think I should come up with some kind of "stub" or something like that. Well, or leave it as it is. The rest works, I don't want to break everything because of this.

In any case, thank you for your time to this problem and many thanks for this script. This is very useful for people without Python programming skills.


Well hope you're able to find a solution down the line.

And happy my script able to help people out! The support is appreciated!

This is really cool. Would it be possible to use this effect along with dialogue bleeps (since both of those effects use the callback parameter)? If so, what would be the best way to combine them? Thanks!

Actually, I just have to call my bleeps function within name_callback. In hindsight, I should've pieced that together much faster...

The effect is pretty cool. Thank you so much for the resource!

hello! i know this is a total shot in the dark on a very old comment but i am right now stuck on this very issue - i can't figure out how to call the bleeps function within name_callback. is there any chance you have a bit of old code to share?

(6 edits) (+1)

In another .rpy file, I have a dictionary that maps character names (the string that is used for the cb_name parameter when defining a Character) to the path of their respective bleep files, which looks something like this:

## name_callback in game/00auto-highlight.rpy uses this dictionary 
## to determine what bleeps to use per character
define bleeps_dict = {"ss" : "audio/bleep023.ogg", "vl" : "audio/bleep003.ogg", "ap" : "audio/bleep006.ogg"}

And in 00auto-highlight.rpy, I tweaked the name_callback function to look like this (please note that I defined a separate audio channel just for text bleeps):

def name_callback(event, interact=True, name=None, **kwargs):
         global speaking_char
         if event == "begin":
             speaking_char = name
         ## text bleeps
         if name in bleeps_dict:
             if event == "show":
                 renpy.music.play(bleeps_dict[name], channel="bleeps", loop=True)
             elif event == "slow_done" or event == "end":
                 renpy.music.stop(channel="bleeps", fadeout=1.0)

If there is a character who shouldn't use bleeps (I defined a separate Character object for my MC's thoughts, for example), then the "if name in bleeps_dict" line above can be changed to something like: 

if name in bleeps_dict and name != "name_of_silent_character": 

If there are several character who don't speak, it might be easier to define a list of names of silent characters and then use "...and name not in silent_characters". 

I think this is all I needed to do. If you have any issues, please feel free to let me know!! ♡

(Also I'm really sorry if the code formatting above is a bit nasty to read with the indentations)

(2 edits)

this is incredibly helpful, wow!! thank you so much for getting back to me, especially on such an old comment of yours - you're the best <3

i had a big followup question here but i solved it so i edited it out lol. tysm!

I'm happy to help!! I hope it works for your project.

I forgot to mention in my last reply that I defined a separate audio channel for the text bleeps (which affects the changes to the name_callback function). That's why I have a channel named "bleeps" in the code. This can be changed to any audio channel (premade or custom) where bleeps can work. 

Please feel free to let me know if you have any more questions about my implementation!! <3 <3

Is there a way to highlight both characters during narration? I am loving this script as a newbie to Renpy, so thank you sm for publishing it.

You're a god for releasing all of these cool stuff, THANK YOU.

how do i set this up?

Simply put the 00auto-highlight.rpy file in the game folder in your project. Instructions on how to use it are provided within the file itself and an example of how to make use of it is provided in script.rpy.

Is it possible to put it in the ren'py folder so all my games benefits from it? Or do I need to put it in every games' folder?

This is so cool, thank you so much!

(1 edit)

holy crap wow


You can also do this by taking your sprite and fading it out in Gimp.  Then when you show your characters, you just show the one that is speaking and the others as the faded out versions.  You can see how I did that in this game = https://rpgmaker.net/games/8212/


Yes, that does work. Though it has some issues. One is that it eats up a lot of unneeded file space. With your method, every image essentially requires twice as much file space. It saves a lot of file space if you do the color manipulation in the engine for basically free. And if you want to change the look of it a little, it's a lot easier this way, since you don't have to re-export every image. Albeit, the color part of this does require using Renpy 7.4, which doesn't work on some older devices.

The other issue is that then in your script, you have to write out every 'show' and 'hide' statement manually to swap between the two. Which is a fair amount of work and means you have to be sure you do it for every time who's speaking changes in the dialogue. Which might be a lot.

With my method, and a bit of setup, it does both of these effects automatically without any additional work needed or taking up additional file space. While I commend your method and think it is clever and certainly good for its time. I think this is a nice alternative for people going forward, if they wish to do it as well.

Hi, if I want to change the contrast instead of saturation, which line I have to change? (I actually have a faint idea but rather not mess with the calculation)

I don't believe Renpy has a class that directly affects contrast. But the list of predefined matrixes can be found here. https://www.renpy.org/doc/html/matrixcolor.html#built-in-colormatrix-subclasses 

As for actually modifying it, you'll be doing that near the bottom of the 00auto-hightlight.rpy. Specifically around lines 186-192. You'll also likely want to add a change amount a bit higher with all the rest of them around 123-125. If you have any other questions I can answer, feel free to ask!

Ah I see, I thought it would be possible seeing this https://www.renpy.org/doc/html/im.html#im.matrix.contrast

but I might playing with available predefined matrixes a bit more

Oh! It would seem there is a ContrastMatrix that isn't mentioned in the matrixcolor documentation. So yeah, using ContrastMatrix should work I believe. Here's the source code to prove it exists.