Polygon Material_index Animation
Random keyframing polygon material indices

Code below run on a default cone with 4 materials of colour: red, green, blue, yellow.

Can set the frame in keyframe insert

All polygons are given material index 0 at frame 1

At the frame 2 times the polygon index plus 10 the polygon changes to a random choice of material slots with material.


Example script.
import bpy
from random import choice
context = bpy.context
ob = context.object
me = ob.data

choices = [i for i, s in enumerate(ob.material_slots)
        if s.material]
        
for p in me.polygons:
    p.material_index = 0
    p.keyframe_insert(&quot;material_index&quot;, frame=1)
    p.material_index = choice(choices)
    p.keyframe_insert(&quot;material_index&quot;, frame=2 * p.index + 10)

Using Drivers


It is a low level version of dynamic painting

In which case here is an example of using a driver instead of keyframing an fcurve.
Example below selects face index based on integer global distance of face center to emtpy.  Have left in test driver method with  test prints
The world matrices of the empty and the mesh object are added as driver variables, and passed to driver method by way of locals.
The image above is on a 16 x 16 grid, takes a while to run.  On my system a 100 x 100 grid works but takes a while and can get unresponsive.
Select the mesh object and run script.
import bpy

context = bpy.context
ob = context.object
me = ob.data

bpy.ops.object.empty_add()
mt = context.object

def add_driver(face):
    face.driver_remove(&quot;material_index&quot;)
    fcurve = face.driver_add(&quot;material_index&quot;)
    
    driver = fcurve.driver
    driver.use_self = True
    driver.expression = &quot;x(self, locals())&quot;
    var = driver.variables.new()
    var.name = &quot;mw&quot;
    var.targets[0].id = ob
    var.targets[0].data_path = &quot;matrix_world&quot;
    
   
    var = driver.variables.new()    
    var.name = &quot;emw&quot;    
    var.targets[0].id = mt
    var.targets[0].data_path = &quot;matrix_world&quot;
    
def x(self, locals):
    print(self)
    print(locals)
    mw = locals[&quot;mw&quot;]
    emw = locals[&quot;emw&quot;]
    d = (mw @ self.center - emw.translation).length
    return min(int(d), 5)

bpy.app.driver_namespace[&quot;x&quot;] = x

for p in me.polygons:
    add_driver(p)

Related
This one randomly sets color of faces of a modified (eg subsurf) mesh using a frame change handler.
https://blender.stackexchange.com/a/183440/15543