A simple light sensor provides a measure of light incident on the photo-resistor. If one were to move a finger to touch and cover the sensor, the reading many range from 0 to 1023. In principle, that would differentiate more than a thousand shades.
In practice, I could reliably position a finger at perhaps 4 different points, from fully covering the sensor to about 2cm away, with 2 differentiable intermediate positions.
The code in the sensor library allows you to set the number of levels you wish to detect, say n, and returns a number between 0 and (n-1). It assumes that an analog sensor is connected to one of Arduino’s analog pins. Of course, the code would work with any analog sensor. The code recalibrates when the readings are held for a period of time (currently set to 10 seconds) that is not the normal at rest value (1 or 0).
To use, include the header file at the beginning of the code:
#include “AACsensors.h”
and then create the object, s:
AnalogSensor s(A0, 500, 0, 0);
where the four arguments are: pin number, the refractory period in milliseconds, normal high or low (1 or 0), debug mode (0 if no debug messages are needed). The refractory period is used so that the reading is stable within that period.
To take a reading,
reading = s.Level(3);
where, in this example, the variable 3 is the number of levels one wishes to distinguish. The number returned is 0, 1 and 2.
There is a corresponding digital sensor function. Normally, one would just take the reading directly. My code for the digital sensor allows for a refractory period to be used, again, in order to obtain a stable reading. To set up, use the same argument signature as above,
DigitalSensor s(0, 500, 0, 0);
To take a reading,
reading = s.Read();
Code repository: https://github.com/AbilitySpectrum/ArduinoAAC
I have used both the wRobot Light Sensor as well as the Minimum Luminence Light Sensor.

