How flicker an image accurately at a given frequency?

I need to flicker an image 2D at a well defined frequency to a SSVEP-BCI application. I tried to use the function WaitForSeconds to change the image on each loop iteration, as follow:

void OnGUI()
	{
		if (cont == 0)
			GUI.DrawTexture (new Rect (0+sizew/2, Screen.height/2 - sizeh/2, sizew, sizeh), checkerboard01);
		else
			GUI.DrawTexture (new Rect (0+sizew/2, Screen.height/2 - sizeh/2, sizew, sizew), checkerboard02);
	}
	
	IEnumerator changeCheckerboard(){
		while (true) {
			if (cont == 0)
				cont = 1;
			else
				cont = 0;
			yield return new WaitForSeconds (frequency);
		}

However, I didn’t get the exactly flickering frequency. Some suggestion, to achieve the accurate rate?

How exact does it need to be?

Consider how realtime game engines work. Let’s say the game runs at 30 FPS. You’d have a maximum accuracy of 1/30th of a second. If it’s running at 200 FPS, you get 1/200th of a second’s worth of accuracy. The faster the game runs in frames-per-second, the more accurately your time-related logic will perform.

The human eye can only detect this so well: Beyond about 60 FPS, the eye cannot perceive any additional accuracy. In most situations, 30 FPS is enough to trick the eye into believing whatever illusion you’re trying to create, whether it’s motion over time or a change in a color or texture.

Even the difference between 1/30th and 2/30ths of a second is so incredibly small, I have a hard time believing it presents any kind of problem here. If you are very confident that you are witnessing a discrepancy in the timing of these events, perhaps there is some other factor at work not visible to us, or the problem is related to a fluctuating framerate. See other questions about framerate to learn how to clamp it to a specific value.

Also you should know that OnGUI is legacy. It is highly ill-advised to use OnGUI for anything except editor scripting.

Best,

@harlei
I am doing the SSVEP-BCI Stimuli generation too. I do not use image or GUI, I draw 3D cubes and change their color in the Update function instead.
My code looks like this, it’s with phase and frequency and has 40 ssvep target with cue target display.
note that the update function was called every time a frame is generated.

void Update () {
//当前线程的执行状态
fps_counter_total = fps_counter_total + 1;

        temp.r = fps_counter_total % 2;
        temp.g = fps_counter_total % 2;
        temp.b = fps_counter_total % 2;
        temp.a = fps_counter_total % 2;
        indicatorcolor.material.color = temp;

    if (fps_counter_total % 368 == 0)
    {
        fps_counter = 0;
        sti_state = 0;
        cue_count = cue_count + 1;
		if (cue_count > 13) {
			cue_count = 0;
			fps_counter=0;
			fps_counter_total=0;
		}
        fps_counter_total = 0;

    }
    else if (fps_counter_total % 368 == 30)
    {
        sti_state = 1;
        temp.r = 1;
        temp.g = 1;
        temp.b = 1;
        temp.a = 1;
        indicatorcolor1.material.color = temp;
    }
    else if (fps_counter_total % 368 == 278)
    {
        sti_state = 2;
     
        temp.r = 0;
        temp.g = 0;
        temp.b = 0;
        temp.a = 0;
        indicatorcolor1.material.color = temp;

    }
    switch (sti_state)
    { case 0:
            for (mi = 0; mi < 40; mi++)
            {

                temp.r = 1;
                temp.g = 1;
                temp.b = 1;
                temp.a = 1;
                mcolor[mi].material.color = temp;
            }
            temp.r = 0;
            temp.g = 1;
            temp.b = 0;
            temp.a = 1;
            mcolor[cue_test[cue_count]].material.color = temp;
            break;
        case 1:

            for (mi = 0; mi < 40; mi++)
            {
                gamma_mod[mi] = (float)((1 + Mathf.Sin((float)((fps_counter / fps) * 2.0 * sti_freq[mi] * Mathf.PI + (sti_phase[mi] * Mathf.PI)))) / 2.0);
                temp.r = gamma_mod[mi];
                temp.g = gamma_mod[mi];
                temp.b = gamma_mod[mi];
                temp.a = 1;
                mcolor[mi].material.color = temp;
            }
            fps_counter = fps_counter + 1;
            break;
        case 2:
            for (mi = 0; mi < 40; mi++)
            {
                gamma_mod[mi] = (float)((1 + Mathf.Sin((float)((fps_counter / fps) * 2.0 * sti_freq[mi] * Mathf.PI + (sti_phase[mi] * Mathf.PI)))) / 2.0);
                temp.r = gamma_mod[mi];
                temp.g = gamma_mod[mi];
                temp.b = gamma_mod[mi];
                temp.a = 1;
                mcolor[mi].material.color = temp;
            }
            temp.r = 0;
            temp.g = 0;
            temp.b = 1;
            temp.a = 1;
            mcolor[cue_test[cue_count]].material.color = temp;
            fps_counter = fps_counter + 1;
            break;
        default:
            break;

    }
    
        
}