.on('changes'... will only work on items being added, removed, or moved. If you're just concerned with visibility, you need to watch the visible property on each layer. Here's an example of how you might do this: JS Bin - Collaborative JavaScript Debugging
There's a few things to note here:
I'm watching for changes on the map.layers collection, because a layer might be added or removed from the map, in which case I want to start watching its visible property (if added) or remove an old watcher (if removed).
I'm using a Map to keep track of watchers. This is just a convenient way to associate a layer to a watcher. You don't have to do this if you don't care about cleaning up old watchers.
I'm using the esri/core/watchUtils watch method in order to check for changes on the visible property on each layer.
You can test this by clicking on the eye icons in the layer list and looking at the console, it should print the layer name and its visibility when it changes. If you want to include basemaps, just change map.layers to map.allLayers everywhere you see it in the sample.