diff --git a/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.cpp b/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.cpp
index 3e5e02feb42e0e1a128a34a0bbb780a9100cf600..688423e02e8ef54c7639d183e0a0510fe93eb86e 100644
--- a/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.cpp
+++ b/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.cpp
@@ -2,12 +2,14 @@
 
 #include <cmath>
 #include <iostream>
+#include <vector>
 #include <algorithm> // std::min, max
-
 #include <cstdlib>
 #include <sys/timeb.h>
 #include <tcptagging/IStimulusSender.h>
 #include <iomanip>
+#include <array>
+#include <functional> // greater
 
 #if defined TARGET_OS_Linux
 #include <unistd.h>
@@ -86,6 +88,8 @@ bool CGrazMultiVisualization::initialize()
 	m_nbPredictionsMin = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 5);
 
 	m_nbModality = (getStaticBoxContext().getSettingCount() - m_NonModalitySettingsCount) / 2;
+	m_barScales.resize(m_nbModality);
+	for (auto& s : m_barScales) { s = 0; }
 	vector<string> paths;
 	paths.reserve(m_nbModality + 1);
 	m_stimulationlist.reserve(m_nbModality);
@@ -291,7 +295,7 @@ void CGrazMultiVisualization::drawReference()
 //---------------------------------------------------------------------------------------------------
 void CGrazMultiVisualization::drawModality()
 {
-	const GdkPixbuf* modality = m_largeImgs[m_modality];
+	const GdkPixbuf* modality = m_largeImgs[(m_showInstruction ? m_modality + 1 : 0)];	// Because the first element is the none instruction
 	const gint w = m_windowW / 2,
 			   h = m_windowH / 2,
 			   x = w - gdk_pixbuf_get_width(modality) / 2,
@@ -305,7 +309,7 @@ void CGrazMultiVisualization::drawBar()
 {
 	if (m_modality != -1)
 	{
-		const gint currX = (m_modality + 1) * m_modalityX, currH = gint(0.5 * m_barH);
+		const gint currX = (m_modality + 1) * m_modalityX, currH = gint(m_barScales[m_modality] < 0 ? 0 : m_barScales[m_modality] * m_barH);
 		gdk_pixbuf_render_to_drawable(m_bar, m_widget->window, nullptr, 0, 0, currX - m_barW / 2, m_modalityY - currH, m_barW, currH, GDK_RGB_DITHER_NONE, 0, 0);
 	}
 }
@@ -396,22 +400,32 @@ void CGrazMultiVisualization::drawAccuracy()
 //***** COMPUTES *****	
 //********************
 //---------------------------------------------------------------------------------------------------
-double CGrazMultiVisualization::aggregatePredictions(const bool all)
+int CGrazMultiVisualization::aggregatePredictions(const bool all)
 {
-	this->getLogManager() << LogLevel_Warning << "aggregatePredictions(const bool all)\n";
-	double vote = 0;
+	int vote = 0;
 	if (m_amplitudes.size() >= m_nbPredictionsMin)
 	{
-		// step backwards with rev iter to take the latest samples
+		// step backwards with rev iter to take the mean of the latest samples
 		uint64_t count = 0;
-		double maxA = -DBL_MAX;
+		vector<double> amp(m_nbModality, 0);
+		//double maxA = -DBL_MAX;
 		for (auto a = m_amplitudes.rbegin(); a != m_amplitudes.rend() && (all || count < m_nbPredictionsMin); ++a, ++count)
 		{
-			vote += *a;
-			maxA = std::max<double>(maxA, abs(*a));
+			for (size_t i = 0; i < a->size(); ++i) { amp[i] += a->at(i); }
+			//maxA = std::max<double>(maxA, abs(*a));
+		}
+		for (auto& a : amp) { a /= count; }
+		// Computes bar scale for each modality, we substract the max value of other modality (other possibility substract the min value or the mean of the values)
+		for (size_t i = 0; i < amp.size(); ++i)
+		{
+			double max = 0;
+			for (size_t j = 0; j < amp.size(); ++j)
+			{
+				if (i != j && max < amp[j]) { max = amp[j]; }
+			}
+			m_barScales[i] = amp[i] - max;
+			if (amp[vote] < amp[i]) { vote = i; }
 		}
-		vote /= maxA;
-		vote /= count;
 	}
 	return vote;
 }
@@ -420,13 +434,11 @@ double CGrazMultiVisualization::aggregatePredictions(const bool all)
 
 //---------------------------------------------------------------------------------------------------
 // @fixme for >2 classes
-void CGrazMultiVisualization::updateConfusionMatrix(const double prediction)
+void CGrazMultiVisualization::updateConfusionMatrix(const int vote)
 {
-	this->getLogManager() << LogLevel_Warning << "updateConfusionMatrix(const double prediction)\n";
 	if (m_modality != -1)
 	{
-		this->getLogManager() << LogLevel_Warning << "Predictions : " << prediction << "\n";
-		//
+		(m_confusion.getBuffer())[m_modality * m_nbModality + vote]++;		// line = m_modality, col = vote
 	}
 	/*
 	if (m_direction == EArrowDirection_Left || m_direction == EArrowDirection_Right)
@@ -444,20 +456,28 @@ void CGrazMultiVisualization::updateConfusionMatrix(const double prediction)
 //---------------------------------------------------------------------------------------------------
 
 //---------------------------------------------------------------------------------------------------
-void CGrazMultiVisualization::setMatrixBuffer(const double* pBuffer)
+void CGrazMultiVisualization::setMatrixBuffer(const double* buffer)
 {
-	this->getLogManager() << LogLevel_Warning << "setMatrixBuffer(const double* pBuffer)\n";
 	if (m_state != 2) { return; }	// No continuous feedback
-	double amp = 0;
 	// Ad-hoc forcing to probability (range [0,1], sum to 1). This will make scaling easier 
 	// if run forever in a continuous mode. If the input is already scaled this way, no effect.
+	double sum = 0;
+	vector<double> values;
+	values.reserve(m_nbModality);
+	for (size_t i = 0; i < m_nbModality; ++i)
+	{
+		values.emplace_back(std::abs(buffer[i]));
+		sum += buffer[i];
+	}
+
+	if (sum != 0) { for (auto& v : values) { v /= sum; } }
+	else { for (auto& v : values) { v = 0.5; } }
 
-	amp = 0.5;
-	m_amplitudes.push_back(amp);
+	m_amplitudes.emplace_back(values);		// Add this buffer to the list
 	if (m_showFeedback && !m_delayFeedback)
 	{
-		m_barScale = aggregatePredictions(false);
-		gdk_window_invalidate_rect(m_widget->window, nullptr, true);
+		aggregatePredictions(false);
+		//gdk_window_invalidate_rect(m_widget->window, nullptr, true);
 	}
 
 	/*
@@ -466,7 +486,7 @@ void CGrazMultiVisualization::setMatrixBuffer(const double* pBuffer)
 		// Ad-hoc forcing to probability (range [0,1], sum to 1). This will make scaling easier 
 		// if run forever in a continuous mode. If the input is already scaled this way, no effect.
 		// 
-		double value0 = std::abs(pBuffer[0]), value1 = std::abs(pBuffer[1]);
+		double value0 = std::abs(buffer[0]), value1 = std::abs(buffer[1]);
 		const double sum = value0 + value1;
 		if (sum != 0)
 		{
@@ -480,7 +500,7 @@ void CGrazMultiVisualization::setMatrixBuffer(const double* pBuffer)
 		}
 		predictedAmplitude = value1 - value0;
 	}
-	else { predictedAmplitude = pBuffer[0]; }
+	else { predictedAmplitude = buffer[0]; }
 
 	m_amplitudes.push_back(predictedAmplitude);
 
@@ -503,14 +523,14 @@ void CGrazMultiVisualization::setStimulation(const uint64_t stimulation)
 			m_state = 0;				// Idle State
 			if (m_showAccuracy || m_delayFeedback)
 			{
-				const double prediction = aggregatePredictions(true);
-				updateConfusionMatrix(prediction);
-				m_barScale = prediction;
+				const int vote = aggregatePredictions(true);
+				updateConfusionMatrix(vote);
+				m_modality = -1;
 			}
 			break;
 		case OVTK_GDF_End_Of_Session:
 			m_state = 0;				// Idle State
-			m_barScale = 0;				// Reinitialize Bar
+			for (auto& s : m_barScales) { s = 0; }
 			drawBar();					// Redraw
 			break;
 		case OVTK_GDF_Cross_On_Screen:
@@ -519,7 +539,7 @@ void CGrazMultiVisualization::setStimulation(const uint64_t stimulation)
 		case OVTK_GDF_Feedback_Continuous:
 			m_state = 2;				// Draw Feedback State
 			m_amplitudes.clear();		// Clear the previous amplitudes
-			m_barScale = 0;				// Bar size 0 to 1
+			for (auto& s : m_barScales) { s = 0; }
 			break;
 		
 			// Modalities stimulations
@@ -530,13 +550,12 @@ void CGrazMultiVisualization::setStimulation(const uint64_t stimulation)
 				if (m_stimulationlist[i] == stimulation) 
 				{ 
 					m_modality = i;
-					m_barScale = 0.5;
+					for (auto& s : m_barScales) { s = 0.5; }
 					break;				// stop the loop (but not so usefull big number of modality is not so big generally)
 				}
 			}
 			if (m_modality != -1)		// If recognize stimulation	
 			{
-				this->getLogManager() << LogLevel_Warning << m_modality << " yeah man\n";
 				m_state = 3;			// Draw Modality State
 			}
 
diff --git a/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.h b/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.h
index 34c477011512455080c9b433bb8d3023cd9e8add..ff93c0894c5128fa71c6674098608608225d9605 100644
--- a/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.h
+++ b/plugins/processing/simple-visualization/src/box-algorithms/ovpCGrazMultiVisualization.h
@@ -58,7 +58,7 @@ namespace OpenViBEPlugins
 			/// <summary>	Update the box state according to the stimulation received.</summary>
 			void setStimulation(uint64_t stimulation);
 
-			void setMatrixBuffer(const double* pBuffer);
+			void setMatrixBuffer(const double* buffer);
 
 			/// <summary> Draw the reference.</summary>
 			/// 
@@ -89,8 +89,8 @@ namespace OpenViBEPlugins
 			
 			/// <summary> Draw the accuracy on top left.</summary>
 			void drawAccuracy();
-			void updateConfusionMatrix(double prediction);
-			double aggregatePredictions(bool all);
+			void updateConfusionMatrix(int vote);
+			int aggregatePredictions(bool all);
 			std::string infos() const;
 
 			//gboolean resizeCB(GtkWidget* widget, GtkAllocation* allocation, void* data) { return TRUE; }
@@ -115,6 +115,7 @@ namespace OpenViBEPlugins
 				 m_barW = 0, m_barH = 0,										// Height/Width of the bar
 				 m_modalityX = 0, m_modalityY = 0,								// Center Bottom Position (x, y) of the first modality (next is in x+(x+margin)
 				 m_modalityW = 0;												// Half Width of the modality : (m_barW + 2 * m_margin) / 2
+			std::vector<double> m_barScales;									// 0 to 1 for each bar (depends of the feedback if only positive feedback or not)
 			double m_barScale = 0.0;											// 0 to 1
 			bool m_needRedraw = false;											// if we need redraw
 			size_t m_state = 0;													// Actual state
@@ -140,7 +141,7 @@ namespace OpenViBEPlugins
 
 			//***** Other *****
 			OpenViBE::CMatrix m_confusion;
-			std::vector<double> m_amplitudes;			// predictions for the current trial
+			std::vector<std::vector<double>> m_amplitudes;	// All amplitudes the current trial
 			uint64_t m_startTime = 0, m_endTime = 0;	//Start and end time of the last buffer
 
 			bool m_twoValueInput = false;